Compare commits

..

No commits in common. "dev" and "v5.11.1" have entirely different histories.
dev ... v5.11.1

292 changed files with 10126 additions and 15903 deletions

View file

@ -9,7 +9,10 @@
"--compile-commands-dir=${workspaceFolder}/out" "--compile-commands-dir=${workspaceFolder}/out"
], ],
"cmake.generator": "Ninja Multi-Config", "cmake.generator": "Ninja Multi-Config",
"cmake.buildDirectory": "${workspaceFolder}/out" "cmake.buildDirectory": "${workspaceFolder}/out",
"cmake.configureSettings": {
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
}, },
"extensions": [ "extensions": [
"ms-vscode.cpptools-extension-pack", "ms-vscode.cpptools-extension-pack",

View file

@ -5,7 +5,7 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
Thanks for reporting issues of AyuGram Desktop! Thanks for reporting issues of Telegram Desktop!
To make it easier for us to help you please enter detailed information below. To make it easier for us to help you please enter detailed information below.
- type: textarea - type: textarea
@ -39,9 +39,12 @@ body:
required: true required: true
- type: input - type: input
attributes: attributes:
label: Version of AyuGram Desktop label: Version of Telegram Desktop
description: > description: >
**Don't use 'latest'**, specify actual version. Please note we don't support versions from Linux distro repositories.
If you need support for these versions, **please contact your distro maintainer**
or your distro bugtracker.
**Don't use 'latest'**, specify actual version, **that's a reason to close your issue**.
validations: validations:
required: true required: true
- type: dropdown - type: dropdown
@ -49,7 +52,11 @@ body:
label: Installation source label: Installation source
multiple: false multiple: false
options: options:
- Binary from GitHub / official Telegram source - Static binary from official website
- Microsoft Store
- Mac App Store
- Flatpak
- Snap
- Other (unofficial) source - Other (unofficial) source
validations: validations:
required: true required: true
@ -58,7 +65,9 @@ body:
label: Crash ID label: Crash ID
description: > description: >
If you're reporting a crash, please enter the crash ID from the crash reporter If you're reporting a crash, please enter the crash ID from the crash reporter
opening on the next launch after crash. opening on the next launch after crash. **You have to enable beta versions
installation in Settings -> Advanced for the reporter to appear.**
You don't have to wait for a beta version to arrive.
- type: textarea - type: textarea
attributes: attributes:
label: Logs label: Logs

Binary file not shown.

Before

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 829 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 926 B

View file

@ -1218,7 +1218,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_privacy_contacts" = "My contacts"; "lng_edit_privacy_contacts" = "My contacts";
"lng_edit_privacy_close_friends" = "Close friends"; "lng_edit_privacy_close_friends" = "Close friends";
"lng_edit_privacy_contacts_and_premium" = "Contacts & Premium"; "lng_edit_privacy_contacts_and_premium" = "Contacts & Premium";
"lng_edit_privacy_paid" = "Paid";
"lng_edit_privacy_contacts_and_miniapps" = "Contacts & Mini Apps"; "lng_edit_privacy_contacts_and_miniapps" = "Contacts & Mini Apps";
"lng_edit_privacy_nobody" = "Nobody"; "lng_edit_privacy_nobody" = "Nobody";
"lng_edit_privacy_premium" = "Premium users"; "lng_edit_privacy_premium" = "Premium users";
@ -1357,13 +1356,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_messages_privacy_premium_about" = "Subscribe now to change this setting and get access to other exclusive features of Telegram Premium."; "lng_messages_privacy_premium_about" = "Subscribe now to change this setting and get access to other exclusive features of Telegram Premium.";
"lng_messages_privacy_premium" = "Only subscribers of {link} can select this option."; "lng_messages_privacy_premium" = "Only subscribers of {link} can select this option.";
"lng_messages_privacy_premium_link" = "Telegram Premium"; "lng_messages_privacy_premium_link" = "Telegram Premium";
"lng_messages_privacy_charge" = "Charge for messages";
"lng_messages_privacy_charge_about" = "Charge a fee for messages from people outside your contacts or those you haven't messaged first.";
"lng_messages_privacy_price" = "Set your price per message";
"lng_messages_privacy_price_about" = "You will receive {percent} of the selected fee ({amount}) for each incoming message.";
"lng_messages_privacy_exceptions" = "Exceptions";
"lng_messages_privacy_remove_fee" = "Remove Fee";
"lng_messages_privacy_remove_about" = "Add users or entire groups who won't be charged for sending messages to you.";
"lng_self_destruct_title" = "Account self-destruction"; "lng_self_destruct_title" = "Account self-destruction";
"lng_self_destruct_description" = "If you don't come online at least once within this period, your account will be deleted along with all groups, messages and contacts."; "lng_self_destruct_description" = "If you don't come online at least once within this period, your account will be deleted along with all groups, messages and contacts.";
@ -2166,15 +2158,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_boost_apply#other" = "{from} boosted the group {count} times"; "lng_action_boost_apply#other" = "{from} boosted the group {count} times";
"lng_action_set_chat_intro" = "{from} added the message below for all empty chats. How?"; "lng_action_set_chat_intro" = "{from} added the message below for all empty chats. How?";
"lng_action_payment_refunded" = "{peer} refunded {amount}"; "lng_action_payment_refunded" = "{peer} refunded {amount}";
"lng_action_paid_message_sent#one" = "You paid {count} Star to {action}";
"lng_action_paid_message_sent#other" = "You paid {count} Stars to {action}";
"lng_action_paid_message_one" = "send a message";
"lng_action_paid_message_some#one" = "send {count} message";
"lng_action_paid_message_some#other" = "send {count} messages";
"lng_action_paid_message_got#one" = "You received {count} Star from {name}";
"lng_action_paid_message_got#other" = "You received {count} Stars from {name}";
"lng_you_paid_stars#one" = "You paid {count} Star.";
"lng_you_paid_stars#other" = "You paid {count} Stars.";
"lng_similar_channels_title" = "Similar channels"; "lng_similar_channels_title" = "Similar channels";
"lng_similar_channels_view_all" = "View all"; "lng_similar_channels_view_all" = "View all";
@ -2681,12 +2664,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_credits_summary_history_entry_inner_in" = "In-App Purchase"; "lng_credits_summary_history_entry_inner_in" = "In-App Purchase";
"lng_credits_summary_balance" = "Balance"; "lng_credits_summary_balance" = "Balance";
"lng_credits_commission" = "{amount} commission"; "lng_credits_commission" = "{amount} commission";
"lng_credits_paid_messages_fee#one" = "Fee for {count} Message";
"lng_credits_paid_messages_fee#other" = "Fee for {count} Messages";
"lng_credits_paid_messages_fee_about" = "You receive {percent} of the price that you charge for each incoming message. {link}";
"lng_credits_paid_messages_fee_about_link" = "Change Fee {emoji}";
"lng_credits_paid_messages_full" = "Full Price";
"lng_credits_premium_gift_duration" = "Duration";
"lng_credits_more_options" = "More Options"; "lng_credits_more_options" = "More Options";
"lng_credits_balance_me" = "your balance"; "lng_credits_balance_me" = "your balance";
"lng_credits_buy_button" = "Buy More Stars"; "lng_credits_buy_button" = "Buy More Stars";
@ -2800,8 +2777,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_credits_small_balance_reaction" = "Buy **Stars** and send them to {channel} to support their posts."; "lng_credits_small_balance_reaction" = "Buy **Stars** and send them to {channel} to support their posts.";
"lng_credits_small_balance_subscribe" = "Buy **Stars** and subscribe to **{channel}** and other channels."; "lng_credits_small_balance_subscribe" = "Buy **Stars** and subscribe to **{channel}** and other channels.";
"lng_credits_small_balance_star_gift" = "Buy **Stars** to send gifts to {user} and other contacts."; "lng_credits_small_balance_star_gift" = "Buy **Stars** to send gifts to {user} and other contacts.";
"lng_credits_small_balance_for_message" = "Buy **Stars** to send messages to {user}.";
"lng_credits_small_balance_for_messages" = "Buy **Stars** to send messages.";
"lng_credits_small_balance_fallback" = "Buy **Stars** to unlock content and services on Telegram."; "lng_credits_small_balance_fallback" = "Buy **Stars** to unlock content and services on Telegram.";
"lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars."; "lng_credits_purchase_blocked" = "Sorry, you can't purchase this item with Telegram Stars.";
"lng_credits_enough" = "You have enough stars at the moment. {link}"; "lng_credits_enough" = "You have enough stars at the moment. {link}";
@ -3335,7 +3310,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_premium_about" = "Give {name} access to exclusive features with Telegram Premium. {features}"; "lng_gift_premium_about" = "Give {name} access to exclusive features with Telegram Premium. {features}";
"lng_gift_premium_features" = "See Features >"; "lng_gift_premium_features" = "See Features >";
"lng_gift_premium_label" = "Premium"; "lng_gift_premium_label" = "Premium";
"lng_gift_premium_by_stars" = "or {amount}";
"lng_gift_stars_subtitle" = "Gift Stars"; "lng_gift_stars_subtitle" = "Gift Stars";
"lng_gift_stars_about" = "Give {name} gifts that can be kept on your profile or converted to Stars. {link}"; "lng_gift_stars_about" = "Give {name} gifts that can be kept on your profile or converted to Stars. {link}";
"lng_gift_stars_link" = "What are Stars >"; "lng_gift_stars_link" = "What are Stars >";
@ -3347,12 +3321,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_send_title" = "Send a Gift"; "lng_gift_send_title" = "Send a Gift";
"lng_gift_send_message" = "Enter Message"; "lng_gift_send_message" = "Enter Message";
"lng_gift_send_anonymous" = "Hide My Name"; "lng_gift_send_anonymous" = "Hide My Name";
"lng_gift_send_pay_with_stars" = "Pay with {amount}";
"lng_gift_send_stars_balance" = "Your balance is {amount}. {link}";
"lng_gift_send_stars_balance_link" = "Get More Stars >";
"lng_gift_send_anonymous_self" = "Hide my name and message from visitors to my profile."; "lng_gift_send_anonymous_self" = "Hide my name and message from visitors to my profile.";
"lng_gift_send_anonymous_about" = "You can hide your name and message from visitors to {user}'s profile. {recipient} will still see your name and message."; "lng_gift_send_anonymous_about" = "You can hide your name and message from visitors to {user}'s profile. {recipient} will still see your name and message.";
"lng_gift_send_anonymous_about_paid" = "You can hide your name from visitors to {user}'s profile. {recipient} will still see your name.";
"lng_gift_send_anonymous_about_channel" = "You can hide your name and message from all visitors of this channel except its admins."; "lng_gift_send_anonymous_about_channel" = "You can hide your name and message from all visitors of this channel except its admins.";
"lng_gift_send_unique" = "Make Unique for {price}"; "lng_gift_send_unique" = "Make Unique for {price}";
"lng_gift_send_unique_about" = "Enable this to let {user} turn your gift into a unique collectible. {link}"; "lng_gift_send_unique_about" = "Enable this to let {user} turn your gift into a unique collectible. {link}";
@ -3428,7 +3398,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_display_done_channel" = "The gift is now shown in channel's Gifts."; "lng_gift_display_done_channel" = "The gift is now shown in channel's Gifts.";
"lng_gift_display_done_hide" = "The gift is now hidden from your profile page."; "lng_gift_display_done_hide" = "The gift is now hidden from your profile page.";
"lng_gift_display_done_hide_channel" = "The gift is now hidden from channel's Gifts."; "lng_gift_display_done_hide_channel" = "The gift is now hidden from channel's Gifts.";
"lng_gift_pinned_done" = "The gift will always be shown on top.";
"lng_gift_got_stars#one" = "You got **{count} Star** for this gift."; "lng_gift_got_stars#one" = "You got **{count} Star** for this gift.";
"lng_gift_got_stars#other" = "You got **{count} Stars** for this gift."; "lng_gift_got_stars#other" = "You got **{count} Stars** for this gift.";
"lng_gift_channel_got#one" = "Channel got **{count} Star** for this gift."; "lng_gift_channel_got#one" = "Channel got **{count} Star** for this gift.";
@ -3487,8 +3456,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_transfer_button_for" = "Transfer for {price}"; "lng_gift_transfer_button_for" = "Transfer for {price}";
"lng_gift_transfer_wear" = "Wear"; "lng_gift_transfer_wear" = "Wear";
"lng_gift_transfer_take_off" = "Take Off"; "lng_gift_transfer_take_off" = "Take Off";
"lng_gift_menu_show" = "Show";
"lng_gift_menu_hide" = "Hide";
"lng_gift_wear_title" = "Wear {name}"; "lng_gift_wear_title" = "Wear {name}";
"lng_gift_wear_about" = "and get these benefits:"; "lng_gift_wear_about" = "and get these benefits:";
"lng_gift_wear_badge_title" = "Radiant Badge"; "lng_gift_wear_badge_title" = "Radiant Badge";
@ -3642,22 +3609,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_new_contact_from_request_group" = "{user} is an admin of {name}, a group you requested to join."; "lng_new_contact_from_request_group" = "{user} is an admin of {name}, a group you requested to join.";
"lng_new_contact_about_status" = "This account uses {emoji} as a custom status next to its\nname. Such emoji statuses are available to all\nsubscribers of {link}."; "lng_new_contact_about_status" = "This account uses {emoji} as a custom status next to its\nname. Such emoji statuses are available to all\nsubscribers of {link}.";
"lng_new_contact_about_status_link" = "Telegram Premium"; "lng_new_contact_about_status_link" = "Telegram Premium";
"lng_new_contact_not_contact" = "Not a contact";
"lng_new_contact_phone_number" = "Phone number";
"lng_new_contact_registration" = "Registration";
"lng_new_contact_common_groups" = "Common groups";
"lng_new_contact_groups#one" = "{count} group {emoji} {arrow}";
"lng_new_contact_groups#other" = "{count} groups {emoji} {arrow}";
"lng_new_contact_not_official" = "Not an official account";
"lng_new_contact_updated_name" = "User updated name {when}";
"lng_new_contact_updated_photo" = "User updated photo {when}";
"lng_new_contact_updated_now" = "less than an hour ago";
"lng_new_contact_updated_hours#one" = "{count} hour ago";
"lng_new_contact_updated_hours#other" = "{count} hours ago";
"lng_new_contact_updated_days#one" = "{count} day ago";
"lng_new_contact_updated_days#other" = "{count} days ago";
"lng_new_contact_updated_months#one" = "{count} month ago";
"lng_new_contact_updated_months#other" = "{count} months ago";
"lng_from_request_title_channel" = "Response to your join request"; "lng_from_request_title_channel" = "Response to your join request";
"lng_from_request_title_group" = "Response to your join request"; "lng_from_request_title_group" = "Response to your join request";
"lng_from_request_body" = "You received this message because you requested to join {name} on {date}."; "lng_from_request_body" = "You received this message because you requested to join {name} on {date}.";
@ -3686,7 +3637,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_send_anonymous_ph" = "Send anonymously..."; "lng_send_anonymous_ph" = "Send anonymously...";
"lng_story_reply_ph" = "Reply privately..."; "lng_story_reply_ph" = "Reply privately...";
"lng_story_comment_ph" = "Comment story..."; "lng_story_comment_ph" = "Comment story...";
"lng_message_paid_ph" = "Message for {amount}";
"lng_send_text_no" = "Text not allowed."; "lng_send_text_no" = "Text not allowed.";
"lng_send_text_no_about" = "The admins of this group only allow sending {types}."; "lng_send_text_no_about" = "The admins of this group only allow sending {types}.";
"lng_send_text_type_and_last" = "{types} and {last}"; "lng_send_text_type_and_last" = "{types} and {last}";
@ -4848,10 +4798,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_boosts_no_restrict" = "Do not restrict boosters"; "lng_rights_boosts_no_restrict" = "Do not restrict boosters";
"lng_rights_boosts_about" = "Turn this on to always allow users who boosted your group to send messages and media."; "lng_rights_boosts_about" = "Turn this on to always allow users who boosted your group to send messages and media.";
"lng_rights_boosts_about_on" = "Choose how many boosts a user must give to the group to bypass restrictions on sending messages."; "lng_rights_boosts_about_on" = "Choose how many boosts a user must give to the group to bypass restrictions on sending messages.";
"lng_rights_charge_stars" = "Charge Stars for Messages";
"lng_rights_charge_stars_about" = "If you turn this on, regular members of the group will have to pay Stars to send messages.";
"lng_rights_charge_price" = "Set price per message";
"lng_rights_charge_price_about" = "Your group will receive {percent} of the selected fee ({amount}) for each incoming message.";
"lng_slowmode_enabled" = "Slow Mode is active.\nYou can send your next message in {left}."; "lng_slowmode_enabled" = "Slow Mode is active.\nYou can send your next message in {left}.";
"lng_slowmode_no_many" = "Slow mode is enabled. You can't send more than one message at a time."; "lng_slowmode_no_many" = "Slow mode is enabled. You can't send more than one message at a time.";
@ -4859,28 +4805,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_slowmode_seconds#one" = "{count} second"; "lng_slowmode_seconds#one" = "{count} second";
"lng_slowmode_seconds#other" = "{count} seconds"; "lng_slowmode_seconds#other" = "{count} seconds";
"lng_payment_confirm_title" = "Confirm payment";
"lng_payment_confirm_text#one" = "{name} charges **{count}** Star per message.";
"lng_payment_confirm_text#other" = "{name} charges **{count}** Stars per message.";
"lng_payment_confirm_amount#one" = "**{count}** Star";
"lng_payment_confirm_amount#other" = "**{count}** Stars";
"lng_payment_confirm_users#one" = "You selected **{count}** user who charge Stars for messages.";
"lng_payment_confirm_users#other" = "You selected **{count}** users who charge Stars for messages.";
"lng_payment_confirm_chats#one" = "You selected **{count}** chat where you pay Stars for messages.";
"lng_payment_confirm_chats#other" = "You selected **{count}** chats where you pay Stars for messages.";
"lng_payment_confirm_sure#one" = "Would you like to pay {amount} to send **{count}** message?";
"lng_payment_confirm_sure#other" = "Would you like to pay {amount} to send **{count}** messages?";
"lng_payment_confirm_dont_ask" = "Don't ask me again";
"lng_payment_confirm_button#one" = "Pay for {count} Message";
"lng_payment_confirm_button#other" = "Pay for {count} Messages";
"lng_payment_bar_text" = "{name} must pay {cost} for each message to you.";
"lng_payment_bar_button" = "Remove Fee";
"lng_payment_refund_title" = "Remove Fee";
"lng_payment_refund_text" = "Are you sure you want to allow {name} to message you for free?";
"lng_payment_refund_also#one" = "Refund already paid {count} Star";
"lng_payment_refund_also#other" = "Refund already paid {count} Stars";
"lng_payment_refund_confirm" = "Confirm";
"lng_rights_gigagroup_title" = "Broadcast group"; "lng_rights_gigagroup_title" = "Broadcast group";
"lng_rights_gigagroup_convert" = "Convert to Broadcast Group"; "lng_rights_gigagroup_convert" = "Convert to Broadcast Group";
"lng_rights_gigagroup_about" = "Broadcast groups can have over 200,000 members, but only admins can send messages in them."; "lng_rights_gigagroup_about" = "Broadcast groups can have over 200,000 members, but only admins can send messages in them.";
@ -5012,9 +4936,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_send_non_premium_message_toast" = "**{user}** only accepts messages from contacts and {link} subscribers."; "lng_send_non_premium_message_toast" = "**{user}** only accepts messages from contacts and {link} subscribers.";
"lng_send_non_premium_message_toast_link" = "Telegram Premium"; "lng_send_non_premium_message_toast_link" = "Telegram Premium";
"lng_send_charges_stars_text" = "{user} charges {amount} for each message.";
"lng_send_charges_stars_go" = "Buy Stars";
"lng_exceptions_list_title" = "Exceptions"; "lng_exceptions_list_title" = "Exceptions";
"lng_removed_list_title" = "Removed users"; "lng_removed_list_title" = "Removed users";

View file

@ -10,7 +10,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop" <Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE" ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A" Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="5.12.3.0" /> Version="5.11.1.0" />
<Properties> <Properties>
<DisplayName>Telegram Desktop</DisplayName> <DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName> <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View file

@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,12,3,0 FILEVERSION 5,11,1,0
PRODUCTVERSION 5,12,3,0 PRODUCTVERSION 5,11,1,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -62,10 +62,10 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Radolyn Labs" VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop" VALUE "FileDescription", "AyuGram Desktop"
VALUE "FileVersion", "5.12.3.0" VALUE "FileVersion", "5.11.1.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2025" VALUE "LegalCopyright", "Copyright (C) 2014-2025"
VALUE "ProductName", "AyuGram Desktop" VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "5.12.3.0" VALUE "ProductVersion", "5.11.1.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,12,3,0 FILEVERSION 5,11,1,0
PRODUCTVERSION 5,12,3,0 PRODUCTVERSION 5,11,1,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -53,10 +53,10 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Radolyn Labs" VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop Updater" VALUE "FileDescription", "AyuGram Desktop Updater"
VALUE "FileVersion", "5.12.3.0" VALUE "FileVersion", "5.11.1.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2025" VALUE "LegalCopyright", "Copyright (C) 2014-2025"
VALUE "ProductName", "AyuGram Desktop" VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "5.12.3.0" VALUE "ProductVersion", "5.11.1.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -149,14 +149,18 @@ void InitFilterLinkHeader(
iconEmoji iconEmoji
).value_or(Ui::FilterIcon::Custom)).active; ).value_or(Ui::FilterIcon::Custom)).active;
const auto isStatic = title.isStatic; const auto isStatic = title.isStatic;
const auto makeContext = [=](Fn<void()> repaint) {
return Core::MarkedTextContext{
.session = &box->peerListUiShow()->session(),
.customEmojiRepaint = std::move(repaint),
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
auto header = Ui::MakeFilterLinkHeader(box, { auto header = Ui::MakeFilterLinkHeader(box, {
.type = type, .type = type,
.title = TitleText(type)(tr::now), .title = TitleText(type)(tr::now),
.about = AboutText(type, title.text), .about = AboutText(type, title.text),
.aboutContext = Core::TextContext({ .makeAboutContext = makeContext,
.session = &box->peerListUiShow()->session(),
.customEmojiLoopLimit = isStatic ? -1 : 0,
}),
.folderTitle = title.text, .folderTitle = title.text,
.folderIcon = icon, .folderIcon = icon,
.badge = (type == Ui::FilterLinkHeaderType::AddingChats .badge = (type == Ui::FilterLinkHeaderType::AddingChats
@ -556,12 +560,16 @@ void ShowImportToast(
text.append('\n').append(phrase(tr::now, lt_count, added)); text.append('\n').append(phrase(tr::now, lt_count, added));
} }
const auto isStatic = title.isStatic; const auto isStatic = title.isStatic;
const auto makeContext = [=](not_null<QWidget*> widget) {
return Core::MarkedTextContext{
.session = &strong->session(),
.customEmojiRepaint = [=] { widget->update(); },
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
strong->showToast({ strong->showToast({
.text = std::move(text), .text = std::move(text),
.textContext = Core::TextContext({ .textContext = makeContext,
.session = &strong->session(),
.customEmojiLoopLimit = isStatic ? -1 : 0,
})
}); });
} }
@ -632,14 +640,18 @@ void ProcessFilterInvite(
raw->setRealContentHeight(box->heightValue()); raw->setRealContentHeight(box->heightValue());
const auto isStatic = title.isStatic; const auto isStatic = title.isStatic;
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = &strong->session(),
.customEmojiRepaint = update,
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
auto owned = Ui::FilterLinkProcessButton( auto owned = Ui::FilterLinkProcessButton(
box, box,
type, type,
title.text, title.text,
Core::TextContext({ makeContext,
.session = &strong->session(),
.customEmojiLoopLimit = isStatic ? -1 : 0,
}),
std::move(badge)); std::move(badge));
const auto button = owned.data(); const auto button = owned.data();
@ -861,14 +873,18 @@ void ProcessFilterRemove(
}, type, title, iconEmoji, rpl::single(0), horizontalFilters); }, type, title, iconEmoji, rpl::single(0), horizontalFilters);
const auto isStatic = title.isStatic; const auto isStatic = title.isStatic;
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = &strong->session(),
.customEmojiRepaint = update,
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
auto owned = Ui::FilterLinkProcessButton( auto owned = Ui::FilterLinkProcessButton(
box, box,
type, type,
title.text, title.text,
Core::TextContext({ makeContext,
.session = &strong->session(),
.customEmojiLoopLimit = isStatic ? -1 : 0,
}),
std::move(badge)); std::move(badge));
const auto button = owned.data(); const auto button = owned.data();

View file

@ -25,7 +25,6 @@ struct SendOptions {
TimeId scheduled = 0; TimeId scheduled = 0;
BusinessShortcutId shortcutId = 0; BusinessShortcutId shortcutId = 0;
EffectId effectId = 0; EffectId effectId = 0;
int starsApproved = 0;
bool silent = false; bool silent = false;
bool handleSupportSwitch = false; bool handleSupportSwitch = false;
bool invertCaption = false; bool invertCaption = false;

View file

@ -90,13 +90,7 @@ constexpr auto kTransactionsLimit = 100;
? peerFromMTP(*tl.data().vstarref_peer()).value ? peerFromMTP(*tl.data().vstarref_peer()).value
: 0; : 0;
const auto incoming = (amount >= StarsAmount()); const auto incoming = (amount >= StarsAmount());
const auto paidMessagesCount const auto saveActorId = (reaction || !extended.empty()) && incoming;
= tl.data().vpaid_messages().value_or_empty();
const auto premiumMonthsForStars
= tl.data().vpremium_gift_months().value_or_empty();
const auto saveActorId = (reaction
|| !extended.empty()
|| paidMessagesCount) && incoming;
const auto parsedGift = stargift const auto parsedGift = stargift
? FromTL(&peer->session(), *stargift) ? FromTL(&peer->session(), *stargift)
: std::optional<Data::StarGift>(); : std::optional<Data::StarGift>();
@ -116,9 +110,9 @@ constexpr auto kTransactionsLimit = 100;
.bareGiftStickerId = giftStickerId, .bareGiftStickerId = giftStickerId,
.bareActorId = saveActorId ? barePeerId : uint64(0), .bareActorId = saveActorId ? barePeerId : uint64(0),
.uniqueGift = parsedGift ? parsedGift->unique : nullptr, .uniqueGift = parsedGift ? parsedGift->unique : nullptr,
.starrefAmount = paidMessagesCount ? StarsAmount() : starrefAmount, .starrefAmount = starrefAmount,
.starrefCommission = paidMessagesCount ? 0 : starrefCommission, .starrefCommission = starrefCommission,
.starrefRecipientId = paidMessagesCount ? 0 : starrefBarePeerId, .starrefRecipientId = starrefBarePeerId,
.peerType = tl.data().vpeer().match([](const HistoryPeerTL &) { .peerType = tl.data().vpeer().match([](const HistoryPeerTL &) {
return Data::CreditsHistoryEntry::PeerType::Peer; return Data::CreditsHistoryEntry::PeerType::Peer;
}, [](const MTPDstarsTransactionPeerPlayMarket &) { }, [](const MTPDstarsTransactionPeerPlayMarket &) {
@ -144,15 +138,9 @@ constexpr auto kTransactionsLimit = 100;
? base::unixtime::parse(tl.data().vtransaction_date()->v) ? base::unixtime::parse(tl.data().vtransaction_date()->v)
: QDateTime(), : QDateTime(),
.successLink = qs(tl.data().vtransaction_url().value_or_empty()), .successLink = qs(tl.data().vtransaction_url().value_or_empty()),
.paidMessagesCount = paidMessagesCount,
.paidMessagesAmount = (paidMessagesCount
? starrefAmount
: StarsAmount()),
.paidMessagesCommission = paidMessagesCount ? starrefCommission : 0,
.starsConverted = int(nonUniqueGift .starsConverted = int(nonUniqueGift
? nonUniqueGift->vconvert_stars().v ? nonUniqueGift->vconvert_stars().v
: 0), : 0),
.premiumMonthsForStars = premiumMonthsForStars,
.floodSkip = int(tl.data().vfloodskip_number().value_or(0)), .floodSkip = int(tl.data().vfloodskip_number().value_or(0)),
.converted = stargift && incoming, .converted = stargift && incoming,
.stargift = stargift.has_value(), .stargift = stargift.has_value(),

View file

@ -114,8 +114,7 @@ void GlobalPrivacy::updateHideReadTime(bool hide) {
archiveAndMuteCurrent(), archiveAndMuteCurrent(),
unarchiveOnNewMessageCurrent(), unarchiveOnNewMessageCurrent(),
hide, hide,
newRequirePremiumCurrent(), newRequirePremiumCurrent());
newChargeStarsCurrent());
} }
bool GlobalPrivacy::hideReadTimeCurrent() const { bool GlobalPrivacy::hideReadTimeCurrent() const {
@ -126,6 +125,14 @@ rpl::producer<bool> GlobalPrivacy::hideReadTime() const {
return _hideReadTime.value(); return _hideReadTime.value();
} }
void GlobalPrivacy::updateNewRequirePremium(bool value) {
update(
archiveAndMuteCurrent(),
unarchiveOnNewMessageCurrent(),
hideReadTimeCurrent(),
value);
}
bool GlobalPrivacy::newRequirePremiumCurrent() const { bool GlobalPrivacy::newRequirePremiumCurrent() const {
return _newRequirePremium.current(); return _newRequirePremium.current();
} }
@ -134,25 +141,6 @@ rpl::producer<bool> GlobalPrivacy::newRequirePremium() const {
return _newRequirePremium.value(); return _newRequirePremium.value();
} }
int GlobalPrivacy::newChargeStarsCurrent() const {
return _newChargeStars.current();
}
rpl::producer<int> GlobalPrivacy::newChargeStars() const {
return _newChargeStars.value();
}
void GlobalPrivacy::updateMessagesPrivacy(
bool requirePremium,
int chargeStars) {
update(
archiveAndMuteCurrent(),
unarchiveOnNewMessageCurrent(),
hideReadTimeCurrent(),
requirePremium,
chargeStars);
}
void GlobalPrivacy::loadPaidReactionShownPeer() { void GlobalPrivacy::loadPaidReactionShownPeer() {
if (_paidReactionShownPeerLoaded) { if (_paidReactionShownPeerLoaded) {
return; return;
@ -181,8 +169,7 @@ void GlobalPrivacy::updateArchiveAndMute(bool value) {
value, value,
unarchiveOnNewMessageCurrent(), unarchiveOnNewMessageCurrent(),
hideReadTimeCurrent(), hideReadTimeCurrent(),
newRequirePremiumCurrent(), newRequirePremiumCurrent());
newChargeStarsCurrent());
} }
void GlobalPrivacy::updateUnarchiveOnNewMessage( void GlobalPrivacy::updateUnarchiveOnNewMessage(
@ -191,16 +178,14 @@ void GlobalPrivacy::updateUnarchiveOnNewMessage(
archiveAndMuteCurrent(), archiveAndMuteCurrent(),
value, value,
hideReadTimeCurrent(), hideReadTimeCurrent(),
newRequirePremiumCurrent(), newRequirePremiumCurrent());
newChargeStarsCurrent());
} }
void GlobalPrivacy::update( void GlobalPrivacy::update(
bool archiveAndMute, bool archiveAndMute,
UnarchiveOnNewMessage unarchiveOnNewMessage, UnarchiveOnNewMessage unarchiveOnNewMessage,
bool hideReadTime, bool hideReadTime,
bool newRequirePremium, bool newRequirePremium) {
int newChargeStars) {
using Flag = MTPDglobalPrivacySettings::Flag; using Flag = MTPDglobalPrivacySettings::Flag;
_api.request(_requestId).cancel(); _api.request(_requestId).cancel();
@ -219,44 +204,35 @@ void GlobalPrivacy::update(
| (hideReadTime ? Flag::f_hide_read_marks : Flag()) | (hideReadTime ? Flag::f_hide_read_marks : Flag())
| ((newRequirePremium && newRequirePremiumAllowed) | ((newRequirePremium && newRequirePremiumAllowed)
? Flag::f_new_noncontact_peers_require_premium ? Flag::f_new_noncontact_peers_require_premium
: Flag()) : Flag());
| Flag::f_noncontact_peers_paid_stars;
_requestId = _api.request(MTPaccount_SetGlobalPrivacySettings( _requestId = _api.request(MTPaccount_SetGlobalPrivacySettings(
MTP_globalPrivacySettings( MTP_globalPrivacySettings(MTP_flags(flags))
MTP_flags(flags),
MTP_long(newChargeStars))
)).done([=](const MTPGlobalPrivacySettings &result) { )).done([=](const MTPGlobalPrivacySettings &result) {
_requestId = 0; _requestId = 0;
apply(result); apply(result);
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {
_requestId = 0; _requestId = 0;
if (error.type() == u"PREMIUM_ACCOUNT_REQUIRED"_q) { if (error.type() == u"PREMIUM_ACCOUNT_REQUIRED"_q) {
update( update(archiveAndMute, unarchiveOnNewMessage, hideReadTime, {});
archiveAndMute,
unarchiveOnNewMessage,
hideReadTime,
false,
0);
} }
}).send(); }).send();
_archiveAndMute = archiveAndMute; _archiveAndMute = archiveAndMute;
_unarchiveOnNewMessage = unarchiveOnNewMessage; _unarchiveOnNewMessage = unarchiveOnNewMessage;
_hideReadTime = hideReadTime; _hideReadTime = hideReadTime;
_newRequirePremium = newRequirePremium; _newRequirePremium = newRequirePremium;
_newChargeStars = newChargeStars;
} }
void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &settings) { void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &data) {
const auto &data = settings.data(); data.match([&](const MTPDglobalPrivacySettings &data) {
_archiveAndMute = data.is_archive_and_mute_new_noncontact_peers(); _archiveAndMute = data.is_archive_and_mute_new_noncontact_peers();
_unarchiveOnNewMessage = data.is_keep_archived_unmuted() _unarchiveOnNewMessage = data.is_keep_archived_unmuted()
? UnarchiveOnNewMessage::None ? UnarchiveOnNewMessage::None
: data.is_keep_archived_folders() : data.is_keep_archived_folders()
? UnarchiveOnNewMessage::NotInFoldersUnmuted ? UnarchiveOnNewMessage::NotInFoldersUnmuted
: UnarchiveOnNewMessage::AnyUnmuted; : UnarchiveOnNewMessage::AnyUnmuted;
_hideReadTime = data.is_hide_read_marks(); _hideReadTime = data.is_hide_read_marks();
_newRequirePremium = data.is_new_noncontact_peers_require_premium(); _newRequirePremium = data.is_new_noncontact_peers_require_premium();
_newChargeStars = data.vnoncontact_peers_paid_stars().value_or_empty(); });
} }
} // namespace Api } // namespace Api

View file

@ -49,28 +49,23 @@ public:
[[nodiscard]] bool hideReadTimeCurrent() const; [[nodiscard]] bool hideReadTimeCurrent() const;
[[nodiscard]] rpl::producer<bool> hideReadTime() const; [[nodiscard]] rpl::producer<bool> hideReadTime() const;
void updateNewRequirePremium(bool value);
[[nodiscard]] bool newRequirePremiumCurrent() const; [[nodiscard]] bool newRequirePremiumCurrent() const;
[[nodiscard]] rpl::producer<bool> newRequirePremium() const; [[nodiscard]] rpl::producer<bool> newRequirePremium() const;
[[nodiscard]] int newChargeStarsCurrent() const;
[[nodiscard]] rpl::producer<int> newChargeStars() const;
void updateMessagesPrivacy(bool requirePremium, int chargeStars);
void loadPaidReactionShownPeer(); void loadPaidReactionShownPeer();
void updatePaidReactionShownPeer(PeerId shownPeer); void updatePaidReactionShownPeer(PeerId shownPeer);
[[nodiscard]] PeerId paidReactionShownPeerCurrent() const; [[nodiscard]] PeerId paidReactionShownPeerCurrent() const;
[[nodiscard]] rpl::producer<PeerId> paidReactionShownPeer() const; [[nodiscard]] rpl::producer<PeerId> paidReactionShownPeer() const;
private: private:
void apply(const MTPGlobalPrivacySettings &settings); void apply(const MTPGlobalPrivacySettings &data);
void update( void update(
bool archiveAndMute, bool archiveAndMute,
UnarchiveOnNewMessage unarchiveOnNewMessage, UnarchiveOnNewMessage unarchiveOnNewMessage,
bool hideReadTime, bool hideReadTime,
bool newRequirePremium, bool newRequirePremium);
int newChargeStars);
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
MTP::Sender _api; MTP::Sender _api;
@ -81,7 +76,6 @@ private:
rpl::variable<bool> _showArchiveAndMute = false; rpl::variable<bool> _showArchiveAndMute = false;
rpl::variable<bool> _hideReadTime = false; rpl::variable<bool> _hideReadTime = false;
rpl::variable<bool> _newRequirePremium = false; rpl::variable<bool> _newRequirePremium = false;
rpl::variable<int> _newChargeStars = 0;
rpl::variable<PeerId> _paidReactionShownPeer = false; rpl::variable<PeerId> _paidReactionShownPeer = false;
std::vector<Fn<void()>> _callbacks; std::vector<Fn<void()>> _callbacks;
bool _paidReactionShownPeerLoaded = false; bool _paidReactionShownPeerLoaded = false;

View file

@ -42,7 +42,7 @@ Polls::Polls(not_null<ApiWrap*> api)
void Polls::create( void Polls::create(
const PollData &data, const PollData &data,
SendAction action, const SendAction &action,
Fn<void()> done, Fn<void()> done,
Fn<void()> fail) { Fn<void()> fail) {
_session->api().sendAction(action); _session->api().sendAction(action);
@ -64,9 +64,6 @@ void Polls::create(
history->startSavingCloudDraft(topicRootId); history->startSavingCloudDraft(topicRootId);
} }
const auto silentPost = ShouldSendSilent(peer, action.options); const auto silentPost = ShouldSendSilent(peer, action.options);
const auto starsPaid = std::min(
peer->starsPerMessageChecked(),
action.options.starsApproved);
if (silentPost) { if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent; sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
} }
@ -79,10 +76,6 @@ void Polls::create(
if (action.options.effectId) { if (action.options.effectId) {
sendFlags |= MTPmessages_SendMedia::Flag::f_effect; sendFlags |= MTPmessages_SendMedia::Flag::f_effect;
} }
if (starsPaid) {
action.options.starsApproved -= starsPaid;
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
}
const auto sendAs = action.options.sendAs; const auto sendAs = action.options.sendAs;
if (sendAs) { if (sendAs) {
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as; sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
@ -105,8 +98,7 @@ void Polls::create(
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()), (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, action.options.shortcutId), Data::ShortcutIdToMTP(_session, action.options.shortcutId),
MTP_long(action.options.effectId), MTP_long(action.options.effectId)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) { ), [=](const MTPUpdates &result, const MTP::Response &response) {
if (clearCloudDraft) { if (clearCloudDraft) {
history->finishSavingCloudDraft( history->finishSavingCloudDraft(

View file

@ -27,7 +27,7 @@ public:
void create( void create(
const PollData &data, const PollData &data,
SendAction action, const SendAction &action,
Fn<void()> done, Fn<void()> done,
Fn<void()> fail); Fn<void()> fail);
void sendVotes( void sendVotes(

View file

@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_text_entities.h" #include "api/api_text_entities.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "base/random.h" #include "base/random.h"
#include "data/data_channel.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
@ -378,15 +377,15 @@ const Data::PremiumSubscriptionOptions &Premium::subscriptionOptions() const {
return _subscriptionOptions; return _subscriptionOptions;
} }
rpl::producer<> Premium::someMessageMoneyRestrictionsResolved() const { rpl::producer<> Premium::somePremiumRequiredResolved() const {
return _someMessageMoneyRestrictionsResolved.events(); return _somePremiumRequiredResolved.events();
} }
void Premium::resolveMessageMoneyRestrictions(not_null<UserData*> user) { void Premium::resolvePremiumRequired(not_null<UserData*> user) {
_resolveMessageMoneyRequiredUsers.emplace(user); _resolvePremiumRequiredUsers.emplace(user);
if (!_messageMoneyRequestScheduled if (!_premiumRequiredRequestScheduled
&& _resolveMessageMoneyRequestedUsers.empty()) { && _resolvePremiumRequestedUsers.empty()) {
_messageMoneyRequestScheduled = true; _premiumRequiredRequestScheduled = true;
crl::on_main(_session, [=] { crl::on_main(_session, [=] {
requestPremiumRequiredSlice(); requestPremiumRequiredSlice();
}); });
@ -394,65 +393,50 @@ void Premium::resolveMessageMoneyRestrictions(not_null<UserData*> user) {
} }
void Premium::requestPremiumRequiredSlice() { void Premium::requestPremiumRequiredSlice() {
_messageMoneyRequestScheduled = false; _premiumRequiredRequestScheduled = false;
if (!_resolveMessageMoneyRequestedUsers.empty() if (!_resolvePremiumRequestedUsers.empty()
|| _resolveMessageMoneyRequiredUsers.empty()) { || _resolvePremiumRequiredUsers.empty()) {
return; return;
} }
constexpr auto kPerRequest = 100; constexpr auto kPerRequest = 100;
auto users = MTP_vector_from_range(_resolveMessageMoneyRequiredUsers auto users = MTP_vector_from_range(_resolvePremiumRequiredUsers
| ranges::views::transform(&UserData::inputUser)); | ranges::views::transform(&UserData::inputUser));
if (users.v.size() > kPerRequest) { if (users.v.size() > kPerRequest) {
auto shortened = users.v; auto shortened = users.v;
shortened.resize(kPerRequest); shortened.resize(kPerRequest);
users = MTP_vector<MTPInputUser>(std::move(shortened)); users = MTP_vector<MTPInputUser>(std::move(shortened));
const auto from = begin(_resolveMessageMoneyRequiredUsers); const auto from = begin(_resolvePremiumRequiredUsers);
_resolveMessageMoneyRequestedUsers = { from, from + kPerRequest }; _resolvePremiumRequestedUsers = { from, from + kPerRequest };
_resolveMessageMoneyRequiredUsers.erase(from, from + kPerRequest); _resolvePremiumRequiredUsers.erase(from, from + kPerRequest);
} else { } else {
_resolveMessageMoneyRequestedUsers _resolvePremiumRequestedUsers
= base::take(_resolveMessageMoneyRequiredUsers); = base::take(_resolvePremiumRequiredUsers);
} }
const auto finish = [=](const QVector<MTPRequirementToContact> &list) { const auto finish = [=](const QVector<MTPBool> &list) {
constexpr auto me = UserDataFlag::MeRequiresPremiumToWrite;
constexpr auto known = UserDataFlag::RequirePremiumToWriteKnown;
constexpr auto mask = me | known;
auto index = 0; auto index = 0;
for (const auto &user : base::take(_resolveMessageMoneyRequestedUsers)) { for (const auto &user : base::take(_resolvePremiumRequestedUsers)) {
const auto set = [&](bool requirePremium, int stars) { const auto require = (index < list.size())
using Flag = UserDataFlag; && mtpIsTrue(list[index++]);
constexpr auto me = Flag::RequiresPremiumToWrite; user->setFlags((user->flags() & ~mask)
constexpr auto known = Flag::MessageMoneyRestrictionsKnown; | known
constexpr auto hasPrem = Flag::HasRequirePremiumToWrite; | (require ? me : UserDataFlag()));
constexpr auto hasStars = Flag::HasStarsPerMessage;
user->setStarsPerMessage(stars);
user->setFlags((user->flags() & ~(me | hasPrem | hasStars))
| known
| (requirePremium ? (me | hasPrem) : Flag())
| (stars ? hasStars : Flag()));
};
if (index >= list.size()) {
set(false, 0);
continue;
}
list[index++].match([&](const MTPDrequirementToContactEmpty &) {
set(false, 0);
}, [&](const MTPDrequirementToContactPremium &) {
set(true, 0);
}, [&](const MTPDrequirementToContactPaidMessages &data) {
set(false, data.vstars_amount().v);
});
} }
if (!_messageMoneyRequestScheduled if (!_premiumRequiredRequestScheduled
&& !_resolveMessageMoneyRequiredUsers.empty()) { && !_resolvePremiumRequiredUsers.empty()) {
_messageMoneyRequestScheduled = true; _premiumRequiredRequestScheduled = true;
crl::on_main(_session, [=] { crl::on_main(_session, [=] {
requestPremiumRequiredSlice(); requestPremiumRequiredSlice();
}); });
} }
_someMessageMoneyRestrictionsResolved.fire({}); _somePremiumRequiredResolved.fire({});
}; };
_session->api().request( _session->api().request(
MTPusers_GetRequirementsToContact(std::move(users)) MTPusers_GetIsPremiumRequiredToContact(std::move(users))
).done([=](const MTPVector<MTPRequirementToContact> &result) { ).done([=](const MTPVector<MTPBool> &result) {
finish(result.v); finish(result.v);
}).fail([=] { }).fail([=] {
finish({}); finish({});
@ -479,14 +463,10 @@ rpl::producer<rpl::no_value, QString> PremiumGiftCodeOptions::request() {
for (const auto &tlOption : result.v) { for (const auto &tlOption : result.v) {
const auto &data = tlOption.data(); const auto &data = tlOption.data();
tlMapOptions[data.vusers().v].push_back(tlOption); tlMapOptions[data.vusers().v].push_back(tlOption);
if (qs(data.vcurrency()) == Ui::kCreditsCurrency) {
continue;
}
const auto token = Token{ data.vusers().v, data.vmonths().v }; const auto token = Token{ data.vusers().v, data.vmonths().v };
_stores[token] = Store{ _stores[token] = Store{
.amount = data.vamount().v, .amount = data.vamount().v,
.currency = qs(data.vcurrency()),
.product = qs(data.vstore_product().value_or_empty()), .product = qs(data.vstore_product().value_or_empty()),
.quantity = data.vstore_quantity().value_or_empty(), .quantity = data.vstore_quantity().value_or_empty(),
}; };
@ -495,14 +475,14 @@ rpl::producer<rpl::no_value, QString> PremiumGiftCodeOptions::request() {
} }
} }
for (const auto &[amount, tlOptions] : tlMapOptions) { for (const auto &[amount, tlOptions] : tlMapOptions) {
if (amount == 1 && _optionsForOnePerson.currencies.empty()) { if (amount == 1 && _optionsForOnePerson.currency.isEmpty()) {
_optionsForOnePerson.currency = qs(
tlOptions.front().data().vcurrency());
for (const auto &option : tlOptions) { for (const auto &option : tlOptions) {
_optionsForOnePerson.months.push_back( _optionsForOnePerson.months.push_back(
option.data().vmonths().v); option.data().vmonths().v);
_optionsForOnePerson.totalCosts.push_back( _optionsForOnePerson.totalCosts.push_back(
option.data().vamount().v); option.data().vamount().v);
_optionsForOnePerson.currencies.push_back(
qs(option.data().vcurrency()));
} }
} }
_subscriptionOptions[amount] = GiftCodesFromTL(tlOptions); _subscriptionOptions[amount] = GiftCodesFromTL(tlOptions);
@ -529,7 +509,7 @@ rpl::producer<rpl::no_value, QString> PremiumGiftCodeOptions::applyPrepaid(
_api.request(MTPpayments_LaunchPrepaidGiveaway( _api.request(MTPpayments_LaunchPrepaidGiveaway(
_peer->input, _peer->input,
MTP_long(prepaidId), MTP_long(prepaidId),
invoice.giveawayCredits invoice.creditsAmount
? Payments::InvoiceCreditsGiveawayToTL(invoice) ? Payments::InvoiceCreditsGiveawayToTL(invoice)
: Payments::InvoicePremiumGiftCodeGiveawayToTL(invoice) : Payments::InvoicePremiumGiftCodeGiveawayToTL(invoice)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
@ -560,7 +540,7 @@ Payments::InvoicePremiumGiftCode PremiumGiftCodeOptions::invoice(
const auto token = Token{ users, months }; const auto token = Token{ users, months };
const auto &store = _stores[token]; const auto &store = _stores[token];
return Payments::InvoicePremiumGiftCode{ return Payments::InvoicePremiumGiftCode{
.currency = store.currency, .currency = _optionsForOnePerson.currency,
.storeProduct = store.product, .storeProduct = store.product,
.randomId = randomId, .randomId = randomId,
.amount = store.amount, .amount = store.amount,
@ -573,15 +553,14 @@ Payments::InvoicePremiumGiftCode PremiumGiftCodeOptions::invoice(
std::vector<GiftOptionData> PremiumGiftCodeOptions::optionsForPeer() const { std::vector<GiftOptionData> PremiumGiftCodeOptions::optionsForPeer() const {
auto result = std::vector<GiftOptionData>(); auto result = std::vector<GiftOptionData>();
if (!_optionsForOnePerson.currencies.empty()) { if (!_optionsForOnePerson.currency.isEmpty()) {
const auto count = int(_optionsForOnePerson.months.size()); const auto count = int(_optionsForOnePerson.months.size());
result.reserve(count); result.reserve(count);
for (auto i = 0; i != count; ++i) { for (auto i = 0; i != count; ++i) {
Assert(i < _optionsForOnePerson.totalCosts.size()); Assert(i < _optionsForOnePerson.totalCosts.size());
Assert(i < _optionsForOnePerson.currencies.size());
result.push_back({ result.push_back({
.cost = _optionsForOnePerson.totalCosts[i], .cost = _optionsForOnePerson.totalCosts[i],
.currency = _optionsForOnePerson.currencies[i], .currency = _optionsForOnePerson.currency,
.months = _optionsForOnePerson.months[i], .months = _optionsForOnePerson.months[i],
}); });
} }
@ -602,7 +581,7 @@ Data::PremiumSubscriptionOptions PremiumGiftCodeOptions::options(int amount) {
MTP_int(_optionsForOnePerson.months[i]), MTP_int(_optionsForOnePerson.months[i]),
MTPstring(), MTPstring(),
MTPint(), MTPint(),
MTP_string(_optionsForOnePerson.currencies[i]), MTP_string(_optionsForOnePerson.currency),
MTP_long(_optionsForOnePerson.totalCosts[i] * amount))); MTP_long(_optionsForOnePerson.totalCosts[i] * amount)));
} }
_subscriptionOptions[amount] = GiftCodesFromTL(tlOptions); _subscriptionOptions[amount] = GiftCodesFromTL(tlOptions);
@ -715,38 +694,28 @@ rpl::producer<rpl::no_value, QString> SponsoredToggle::setToggled(bool v) {
}; };
} }
MessageMoneyRestriction ResolveMessageMoneyRestrictions( RequirePremiumState ResolveRequiresPremiumToWrite(
not_null<PeerData*> peer, not_null<PeerData*> peer,
History *maybeHistory) { History *maybeHistory) {
if (const auto channel = peer->asChannel()) {
return {
.starsPerMessage = channel->starsPerMessageChecked(),
.known = true,
};
}
const auto user = peer->asUser(); const auto user = peer->asUser();
if (!user) { if (!user
return { .known = true }; || !user->someRequirePremiumToWrite()
} else if (user->messageMoneyRestrictionsKnown()) { || user->session().premium()) {
return { return RequirePremiumState::No;
.starsPerMessage = user->starsPerMessageChecked(), } else if (user->requirePremiumToWriteKnown()) {
.premiumRequired = (user->requiresPremiumToWrite() return user->meRequiresPremiumToWrite()
&& !user->session().premium()), ? RequirePremiumState::Yes
.known = true, : RequirePremiumState::No;
};
} else if (user->hasStarsPerMessage()) {
return {};
} else if (!user->hasRequirePremiumToWrite()) {
return { .known = true };
} else if (user->flags() & UserDataFlag::MutualContact) { } else if (user->flags() & UserDataFlag::MutualContact) {
return { .known = true }; return RequirePremiumState::No;
} else if (!maybeHistory) { } else if (!maybeHistory) {
return {}; return RequirePremiumState::Unknown;
} }
const auto update = [&](bool require) { const auto update = [&](bool require) {
using Flag = UserDataFlag; using Flag = UserDataFlag;
constexpr auto known = Flag::MessageMoneyRestrictionsKnown; constexpr auto known = Flag::RequirePremiumToWriteKnown;
constexpr auto me = Flag::RequiresPremiumToWrite; constexpr auto me = Flag::MeRequiresPremiumToWrite;
user->setFlags((user->flags() & ~me) user->setFlags((user->flags() & ~me)
| known | known
| (require ? me : Flag())); | (require ? me : Flag()));
@ -758,19 +727,16 @@ MessageMoneyRestriction ResolveMessageMoneyRestrictions(
const auto item = view->data(); const auto item = view->data();
if (!item->out() && !item->isService()) { if (!item->out() && !item->isService()) {
update(false); update(false);
return { .known = true }; return RequirePremiumState::No;
} }
} }
} }
if (user->isContact() // Here we know, that we're not in his contacts. if (user->isContact() // Here we know, that we're not in his contacts.
&& maybeHistory->loadedAtTop() // And no incoming messages. && maybeHistory->loadedAtTop() // And no incoming messages.
&& maybeHistory->loadedAtBottom()) { && maybeHistory->loadedAtBottom()) {
return { update(true);
.premiumRequired = !user->session().premium(),
.known = true,
};
} }
return {}; return RequirePremiumState::Unknown;
} }
rpl::producer<DocumentData*> RandomHelloStickerValue( rpl::producer<DocumentData*> RandomHelloStickerValue(
@ -904,7 +870,6 @@ std::optional<Data::SavedStarGift> FromTL(
.date = data.vdate().v, .date = data.vdate().v,
.upgradable = data.is_can_upgrade(), .upgradable = data.is_can_upgrade(),
.anonymous = data.is_name_hidden(), .anonymous = data.is_name_hidden(),
.pinned = data.is_pinned_to_top(),
.hidden = data.is_unsaved(), .hidden = data.is_unsaved(),
.mine = to->isSelf(), .mine = to->isSelf(),
}; };

View file

@ -116,9 +116,8 @@ public:
[[nodiscard]] auto subscriptionOptions() const [[nodiscard]] auto subscriptionOptions() const
-> const Data::PremiumSubscriptionOptions &; -> const Data::PremiumSubscriptionOptions &;
[[nodiscard]] auto someMessageMoneyRestrictionsResolved() const [[nodiscard]] rpl::producer<> somePremiumRequiredResolved() const;
-> rpl::producer<>; void resolvePremiumRequired(not_null<UserData*> user);
void resolveMessageMoneyRestrictions(not_null<UserData*> user);
private: private:
void reloadPromo(); void reloadPromo();
@ -167,10 +166,10 @@ private:
Data::PremiumSubscriptionOptions _subscriptionOptions; Data::PremiumSubscriptionOptions _subscriptionOptions;
rpl::event_stream<> _someMessageMoneyRestrictionsResolved; rpl::event_stream<> _somePremiumRequiredResolved;
base::flat_set<not_null<UserData*>> _resolveMessageMoneyRequiredUsers; base::flat_set<not_null<UserData*>> _resolvePremiumRequiredUsers;
base::flat_set<not_null<UserData*>> _resolveMessageMoneyRequestedUsers; base::flat_set<not_null<UserData*>> _resolvePremiumRequestedUsers;
bool _messageMoneyRequestScheduled = false; bool _premiumRequiredRequestScheduled = false;
}; };
@ -209,7 +208,6 @@ private:
}; };
struct Store final { struct Store final {
uint64 amount = 0; uint64 amount = 0;
QString currency;
QString product; QString product;
int quantity = 0; int quantity = 0;
}; };
@ -220,7 +218,7 @@ private:
struct { struct {
std::vector<int> months; std::vector<int> months;
std::vector<int64> totalCosts; std::vector<int64> totalCosts;
std::vector<QString> currencies; QString currency;
} _optionsForOnePerson; } _optionsForOnePerson;
std::vector<int> _availablePresets; std::vector<int> _availablePresets;
@ -246,20 +244,12 @@ private:
}; };
struct MessageMoneyRestriction { enum class RequirePremiumState {
int starsPerMessage = 0; Unknown,
bool premiumRequired = false; Yes,
bool known = false; No,
explicit operator bool() const {
return starsPerMessage != 0 || premiumRequired;
}
friend inline bool operator==(
const MessageMoneyRestriction &,
const MessageMoneyRestriction &) = default;
}; };
[[nodiscard]] MessageMoneyRestriction ResolveMessageMoneyRestrictions( [[nodiscard]] RequirePremiumState ResolveRequiresPremiumToWrite(
not_null<PeerData*> peer, not_null<PeerData*> peer,
History *maybeHistory); History *maybeHistory);

View file

@ -26,7 +26,7 @@ Data::PremiumSubscriptionOption CreateSubscriptionOption(
}(); }();
return { return {
.duration = Ui::FormatTTL(months * 86400 * 31), .duration = Ui::FormatTTL(months * 86400 * 31),
.discount = (discount > 0) .discount = discount
? QString::fromUtf8("\xe2\x88\x92%1%").arg(discount) ? QString::fromUtf8("\xe2\x88\x92%1%").arg(discount)
: QString(), : QString(),
.costPerMonth = Ui::FillAmountAndCurrency( .costPerMonth = Ui::FillAmountAndCurrency(

View file

@ -24,26 +24,15 @@ template<typename Option>
if (tlOpts.isEmpty()) { if (tlOpts.isEmpty()) {
return {}; return {};
} }
auto monthlyAmountPerCurrency = base::flat_map<QString, int>();
auto result = Data::PremiumSubscriptionOptions(); auto result = Data::PremiumSubscriptionOptions();
const auto monthlyAmount = [&](const QString &currency) -> int { const auto monthlyAmount = [&] {
const auto it = monthlyAmountPerCurrency.find(currency);
if (it != end(monthlyAmountPerCurrency)) {
return it->second;
}
const auto &min = ranges::min_element( const auto &min = ranges::min_element(
tlOpts, tlOpts,
ranges::less(), ranges::less(),
[&](const Option &o) { [](const Option &o) { return o.data().vamount().v; }
return currency == qs(o.data().vcurrency())
? o.data().vamount().v
: std::numeric_limits<int64_t>::max();
}
)->data(); )->data();
const auto monthly = min.vamount().v / float64(min.vmonths().v); return min.vamount().v / float64(min.vmonths().v);
monthlyAmountPerCurrency.emplace(currency, monthly); }();
return monthly;
};
result.reserve(tlOpts.size()); result.reserve(tlOpts.size());
for (const auto &tlOption : tlOpts) { for (const auto &tlOption : tlOpts) {
const auto &option = tlOption.data(); const auto &option = tlOption.data();
@ -56,7 +45,7 @@ template<typename Option>
const auto currency = qs(option.vcurrency()); const auto currency = qs(option.vcurrency());
result.push_back(CreateSubscriptionOption( result.push_back(CreateSubscriptionOption(
months, months,
monthlyAmount(currency), monthlyAmount,
amount, amount,
currency, currency,
botUrl)); botUrl));

View file

@ -95,9 +95,7 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
const auto messagePostAuthor = peer->isBroadcast() const auto messagePostAuthor = peer->isBroadcast()
? session->user()->name() ? session->user()->name()
: QString(); : QString();
const auto starsPaid = std::min(
peer->starsPerMessageChecked(),
action.options.starsApproved);
if (action.options.scheduled) { if (action.options.scheduled) {
flags |= MessageFlag::IsOrWasScheduled; flags |= MessageFlag::IsOrWasScheduled;
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date; sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
@ -113,10 +111,6 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
flags |= MessageFlag::InvertMedia; flags |= MessageFlag::InvertMedia;
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media; sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
} }
if (starsPaid) {
action.options.starsApproved -= starsPaid;
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
}
auto &histories = history->owner().histories(); auto &histories = history->owner().histories();
histories.sendPreparedMessage( histories.sendPreparedMessage(
@ -135,8 +129,7 @@ void SendSimpleMedia(SendAction action, MTPInputMedia inputMedia) {
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()), (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId), Data::ShortcutIdToMTP(session, action.options.shortcutId),
MTP_long(action.options.effectId), MTP_long(action.options.effectId)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) { ), [=](const MTPUpdates &result, const MTP::Response &response) {
}, [=](const MTP::Error &error, const MTP::Response &response) { }, [=](const MTP::Error &error, const MTP::Response &response) {
api->sendMessageFail(error, peer, randomId); api->sendMessageFail(error, peer, randomId);
@ -167,7 +160,7 @@ void SendExistingMedia(
? (*localMessageId) ? (*localMessageId)
: session->data().nextLocalMessageId()); : session->data().nextLocalMessageId());
const auto randomId = base::RandomValue<uint64>(); const auto randomId = base::RandomValue<uint64>();
auto &action = message.action; const auto &action = message.action;
auto flags = NewMessageFlags(peer); auto flags = NewMessageFlags(peer);
auto sendFlags = MTPmessages_SendMedia::Flags(0); auto sendFlags = MTPmessages_SendMedia::Flags(0);
@ -197,9 +190,7 @@ void SendExistingMedia(
sendFlags |= MTPmessages_SendMedia::Flag::f_entities; sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
} }
const auto captionText = caption.text; const auto captionText = caption.text;
const auto starsPaid = std::min(
peer->starsPerMessageChecked(),
action.options.starsApproved);
if (action.options.scheduled) { if (action.options.scheduled) {
flags |= MessageFlag::IsOrWasScheduled; flags |= MessageFlag::IsOrWasScheduled;
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date; sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
@ -215,10 +206,6 @@ void SendExistingMedia(
flags |= MessageFlag::InvertMedia; flags |= MessageFlag::InvertMedia;
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media; sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
} }
if (starsPaid) {
action.options.starsApproved -= starsPaid;
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
}
session->data().registerMessageRandomId(randomId, newId); session->data().registerMessageRandomId(randomId, newId);
@ -229,7 +216,6 @@ void SendExistingMedia(
.replyTo = action.replyTo, .replyTo = action.replyTo,
.date = NewMessageDate(action.options), .date = NewMessageDate(action.options),
.shortcutId = action.options.shortcutId, .shortcutId = action.options.shortcutId,
.starsPaid = starsPaid,
.postAuthor = NewMessagePostAuthor(action), .postAuthor = NewMessagePostAuthor(action),
.effectId = action.options.effectId, .effectId = action.options.effectId,
}, media, caption); }, media, caption);
@ -254,8 +240,7 @@ void SendExistingMedia(
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()), (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId), Data::ShortcutIdToMTP(session, action.options.shortcutId),
MTP_long(action.options.effectId), MTP_long(action.options.effectId)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) { ), [=](const MTPUpdates &result, const MTP::Response &response) {
}, [=](const MTP::Error &error, const MTP::Response &response) { }, [=](const MTP::Error &error, const MTP::Response &response) {
if (error.code() == 400 if (error.code() == 400
@ -356,7 +341,7 @@ bool SendDice(MessageToSend &message) {
message.action.generateLocal = true; message.action.generateLocal = true;
auto &action = message.action; const auto &action = message.action;
api->sendAction(action); api->sendAction(action);
const auto newId = FullMsgId( const auto newId = FullMsgId(
@ -395,13 +380,6 @@ bool SendDice(MessageToSend &message) {
flags |= MessageFlag::InvertMedia; flags |= MessageFlag::InvertMedia;
sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media; sendFlags |= MTPmessages_SendMedia::Flag::f_invert_media;
} }
const auto starsPaid = std::min(
peer->starsPerMessageChecked(),
action.options.starsApproved);
if (starsPaid) {
action.options.starsApproved -= starsPaid;
sendFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
}
session->data().registerMessageRandomId(randomId, newId); session->data().registerMessageRandomId(randomId, newId);
@ -412,7 +390,6 @@ bool SendDice(MessageToSend &message) {
.replyTo = action.replyTo, .replyTo = action.replyTo,
.date = NewMessageDate(action.options), .date = NewMessageDate(action.options),
.shortcutId = action.options.shortcutId, .shortcutId = action.options.shortcutId,
.starsPaid = starsPaid,
.postAuthor = NewMessagePostAuthor(action), .postAuthor = NewMessagePostAuthor(action),
.effectId = action.options.effectId, .effectId = action.options.effectId,
}, TextWithEntities(), MTP_messageMediaDice( }, TextWithEntities(), MTP_messageMediaDice(
@ -434,8 +411,7 @@ bool SendDice(MessageToSend &message) {
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()), (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId), Data::ShortcutIdToMTP(session, action.options.shortcutId),
MTP_long(action.options.effectId), MTP_long(action.options.effectId)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) { ), [=](const MTPUpdates &result, const MTP::Response &response) {
}, [=](const MTP::Error &error, const MTP::Response &response) { }, [=](const MTP::Error &error, const MTP::Response &response) {
api->sendMessageFail(error, peer, randomId, newId); api->sendMessageFail(error, peer, randomId, newId);
@ -634,9 +610,6 @@ void SendConfirmedFile(
.replyTo = file->to.replyTo, .replyTo = file->to.replyTo,
.date = NewMessageDate(file->to.options), .date = NewMessageDate(file->to.options),
.shortcutId = file->to.options.shortcutId, .shortcutId = file->to.options.shortcutId,
.starsPaid = std::min(
history->peer->starsPerMessageChecked(),
file->to.options.starsApproved),
.postAuthor = NewMessagePostAuthor(action), .postAuthor = NewMessagePostAuthor(action),
.groupedId = groupId, .groupedId = groupId,
.effectId = file->to.options.effectId, .effectId = file->to.options.effectId,

View file

@ -1227,8 +1227,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
MTPint(), // quick_reply_shortcut_id MTPint(), // quick_reply_shortcut_id
MTPlong(), // effect MTPlong(), // effect
MTPFactCheck(), MTPFactCheck(),
MTPint(), // report_delivery_until_date MTPint()), // report_delivery_until_date
MTPlong()), // paid_message_stars
MessageFlags(), MessageFlags(),
NewMessageType::Unread); NewMessageType::Unread);
} break; } break;
@ -1266,8 +1265,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
MTPint(), // quick_reply_shortcut_id MTPint(), // quick_reply_shortcut_id
MTPlong(), // effect MTPlong(), // effect
MTPFactCheck(), MTPFactCheck(),
MTPint(), // report_delivery_until_date MTPint()), // report_delivery_until_date
MTPlong()), // paid_message_stars
MessageFlags(), MessageFlags(),
NewMessageType::Unread); NewMessageType::Unread);
} break; } break;

View file

@ -210,7 +210,6 @@ MTPInputPrivacyKey KeyToTL(UserPrivacy::Key key) {
case Key::About: return MTP_inputPrivacyKeyAbout(); case Key::About: return MTP_inputPrivacyKeyAbout();
case Key::Birthday: return MTP_inputPrivacyKeyBirthday(); case Key::Birthday: return MTP_inputPrivacyKeyBirthday();
case Key::GiftsAutoSave: return MTP_inputPrivacyKeyStarGiftsAutoSave(); case Key::GiftsAutoSave: return MTP_inputPrivacyKeyStarGiftsAutoSave();
case Key::NoPaidMessages: return MTP_inputPrivacyKeyNoPaidMessages();
} }
Unexpected("Key in Api::UserPrivacy::KetToTL."); Unexpected("Key in Api::UserPrivacy::KetToTL.");
} }
@ -242,8 +241,6 @@ std::optional<UserPrivacy::Key> TLToKey(mtpTypeId type) {
case mtpc_inputPrivacyKeyBirthday: return Key::Birthday; case mtpc_inputPrivacyKeyBirthday: return Key::Birthday;
case mtpc_privacyKeyStarGiftsAutoSave: case mtpc_privacyKeyStarGiftsAutoSave:
case mtpc_inputPrivacyKeyStarGiftsAutoSave: return Key::GiftsAutoSave; case mtpc_inputPrivacyKeyStarGiftsAutoSave: return Key::GiftsAutoSave;
case mtpc_privacyKeyNoPaidMessages:
case mtpc_inputPrivacyKeyNoPaidMessages: return Key::NoPaidMessages;
} }
return std::nullopt; return std::nullopt;
} }

View file

@ -32,7 +32,6 @@ public:
About, About,
Birthday, Birthday,
GiftsAutoSave, GiftsAutoSave,
NoPaidMessages,
}; };
enum class Option { enum class Option {
Everyone, Everyone,

View file

@ -546,7 +546,6 @@ void ApiWrap::sendMessageFail(
uint64 randomId, uint64 randomId,
FullMsgId itemId) { FullMsgId itemId) {
const auto show = ShowForPeer(peer); const auto show = ShowForPeer(peer);
const auto paidStarsPrefix = u"ALLOW_PAYMENT_REQUIRED_"_q;
if (show && error == u"PEER_FLOOD"_q) { if (show && error == u"PEER_FLOOD"_q) {
show->showBox( show->showBox(
Ui::MakeInformBox( Ui::MakeInformBox(
@ -601,19 +600,6 @@ void ApiWrap::sendMessageFail(
if (show) { if (show) {
show->showToast(tr::lng_error_schedule_limit(tr::now)); show->showToast(tr::lng_error_schedule_limit(tr::now));
} }
} else if (error.startsWith(paidStarsPrefix)) {
if (show) {
show->showToast(
u"Payment requirements changed. Please, try again."_q);
}
if (const auto stars = error.mid(paidStarsPrefix.size()).toInt()) {
if (const auto user = peer->asUser()) {
user->setStarsPerMessage(stars);
} else if (const auto channel = peer->asChannel()) {
channel->setStarsPerMessage(stars);
}
}
peer->updateFull();
} }
if (const auto item = _session->data().message(itemId)) { if (const auto item = _session->data().message(itemId)) {
Assert(randomId != 0); Assert(randomId != 0);
@ -3356,7 +3342,7 @@ void ApiWrap::finishForwarding(const SendAction &action) {
void ApiWrap::forwardMessages( void ApiWrap::forwardMessages(
Data::ResolvedForwardDraft &&draft, Data::ResolvedForwardDraft &&draft,
SendAction action, const SendAction &action,
FnMut<void()> &&successCallback) { FnMut<void()> &&successCallback) {
Expects(!draft.items.empty()); Expects(!draft.items.empty());
@ -3431,17 +3417,9 @@ void ApiWrap::forwardMessages(
const auto requestType = Data::Histories::RequestType::Send; const auto requestType = Data::Histories::RequestType::Send;
const auto idsCopy = localIds; const auto idsCopy = localIds;
const auto scheduled = action.options.scheduled; const auto scheduled = action.options.scheduled;
const auto starsPaid = std::min(
action.options.starsApproved,
int(ids.size() * peer->starsPerMessageChecked()));
auto oneFlags = sendFlags;
if (starsPaid) {
action.options.starsApproved -= starsPaid;
oneFlags |= SendFlag::f_allow_paid_stars;
}
histories.sendRequest(history, requestType, [=](Fn<void()> finish) { histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_ForwardMessages( history->sendRequestId = request(MTPmessages_ForwardMessages(
MTP_flags(oneFlags), MTP_flags(sendFlags),
forwardFrom->input, forwardFrom->input,
MTP_vector<MTPint>(ids), MTP_vector<MTPint>(ids),
MTP_vector<MTPlong>(randomIds), MTP_vector<MTPlong>(randomIds),
@ -3450,8 +3428,7 @@ void ApiWrap::forwardMessages(
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()), (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, action.options.shortcutId), Data::ShortcutIdToMTP(_session, action.options.shortcutId),
MTPint(), // video_timestamp MTPint() // video_timestamp
MTP_long(starsPaid)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
if (!scheduled) { if (!scheduled) {
this->updates().checkForSentToScheduled(result); this->updates().checkForSentToScheduled(result);
@ -3503,7 +3480,6 @@ void ApiWrap::forwardMessages(
.replyTo = { .topicRootId = topMsgId }, .replyTo = { .topicRootId = topMsgId },
.date = NewMessageDate(action.options), .date = NewMessageDate(action.options),
.shortcutId = action.options.shortcutId, .shortcutId = action.options.shortcutId,
.starsPaid = action.options.starsApproved,
.postAuthor = NewMessagePostAuthor(action), .postAuthor = NewMessagePostAuthor(action),
// forwarded messages don't have effects // forwarded messages don't have effects
@ -3597,7 +3573,6 @@ void ApiWrap::sendSharedContact(
.replyTo = action.replyTo, .replyTo = action.replyTo,
.date = NewMessageDate(action.options), .date = NewMessageDate(action.options),
.shortcutId = action.options.shortcutId, .shortcutId = action.options.shortcutId,
.starsPaid = action.options.starsApproved,
.postAuthor = NewMessagePostAuthor(action), .postAuthor = NewMessagePostAuthor(action),
.effectId = action.options.effectId, .effectId = action.options.effectId,
}, TextWithEntities(), MTP_messageMediaContact( }, TextWithEntities(), MTP_messageMediaContact(
@ -3969,14 +3944,6 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
sendFlags |= MTPmessages_SendMessage::Flag::f_effect; sendFlags |= MTPmessages_SendMessage::Flag::f_effect;
mediaFlags |= MTPmessages_SendMedia::Flag::f_effect; mediaFlags |= MTPmessages_SendMedia::Flag::f_effect;
} }
const auto starsPaid = std::min(
peer->starsPerMessageChecked(),
action.options.starsApproved);
if (starsPaid) {
action.options.starsApproved -= starsPaid;
sendFlags |= MTPmessages_SendMessage::Flag::f_allow_paid_stars;
mediaFlags |= MTPmessages_SendMedia::Flag::f_allow_paid_stars;
}
lastMessage = history->addNewLocalMessage({ lastMessage = history->addNewLocalMessage({
.id = newId.msg, .id = newId.msg,
.flags = flags, .flags = flags,
@ -3984,7 +3951,6 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
.replyTo = action.replyTo, .replyTo = action.replyTo,
.date = NewMessageDate(action.options), .date = NewMessageDate(action.options),
.shortcutId = action.options.shortcutId, .shortcutId = action.options.shortcutId,
.starsPaid = starsPaid,
.postAuthor = NewMessagePostAuthor(action), .postAuthor = NewMessagePostAuthor(action),
.effectId = action.options.effectId, .effectId = action.options.effectId,
}, sending, media); }, sending, media);
@ -4035,8 +4001,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()), (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
mtpShortcut, mtpShortcut,
MTP_long(action.options.effectId), MTP_long(action.options.effectId)
MTP_long(starsPaid)
), done, fail); ), done, fail);
} else { } else {
histories.sendPreparedMessage( histories.sendPreparedMessage(
@ -4054,8 +4019,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()), (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
mtpShortcut, mtpShortcut,
MTP_long(action.options.effectId), MTP_long(action.options.effectId)
MTP_long(starsPaid)
), done, fail); ), done, fail);
} }
isFirst = false; isFirst = false;
@ -4120,7 +4084,7 @@ void ApiWrap::sendBotStart(
void ApiWrap::sendInlineResult( void ApiWrap::sendInlineResult(
not_null<UserData*> bot, not_null<UserData*> bot,
not_null<InlineBots::Result*> data, not_null<InlineBots::Result*> data,
SendAction action, const SendAction &action,
std::optional<MsgId> localMessageId, std::optional<MsgId> localMessageId,
Fn<void(bool)> done) { Fn<void(bool)> done) {
sendAction(action); sendAction(action);
@ -4160,13 +4124,6 @@ void ApiWrap::sendInlineResult(
if (action.options.hideViaBot) { if (action.options.hideViaBot) {
sendFlags |= SendFlag::f_hide_via; sendFlags |= SendFlag::f_hide_via;
} }
const auto starsPaid = std::min(
peer->starsPerMessageChecked(),
action.options.starsApproved);
if (starsPaid) {
action.options.starsApproved -= starsPaid;
sendFlags |= SendFlag::f_allow_paid_stars;
}
const auto sendAs = action.options.sendAs; const auto sendAs = action.options.sendAs;
if (sendAs) { if (sendAs) {
@ -4181,7 +4138,6 @@ void ApiWrap::sendInlineResult(
.replyTo = action.replyTo, .replyTo = action.replyTo,
.date = NewMessageDate(action.options), .date = NewMessageDate(action.options),
.shortcutId = action.options.shortcutId, .shortcutId = action.options.shortcutId,
.starsPaid = starsPaid,
.viaBotId = ((bot && !action.options.hideViaBot) .viaBotId = ((bot && !action.options.hideViaBot)
? peerToUser(bot->id) ? peerToUser(bot->id)
: UserId()), : UserId()),
@ -4205,8 +4161,7 @@ void ApiWrap::sendInlineResult(
MTP_string(data->getId()), MTP_string(data->getId()),
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()), (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, action.options.shortcutId), Data::ShortcutIdToMTP(_session, action.options.shortcutId)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) { ), [=](const MTPUpdates &result, const MTP::Response &response) {
history->finishSavingCloudDraft( history->finishSavingCloudDraft(
topicRootId, topicRootId,
@ -4343,7 +4298,6 @@ void ApiWrap::sendMediaWithRandomId(
const auto history = item->history(); const auto history = item->history();
const auto replyTo = item->replyTo(); const auto replyTo = item->replyTo();
const auto peer = history->peer;
auto caption = item->originalText(); auto caption = item->originalText();
TextUtilities::Trim(caption); TextUtilities::Trim(caption);
@ -4353,12 +4307,6 @@ void ApiWrap::sendMediaWithRandomId(
Api::ConvertOption::SkipLocal); Api::ConvertOption::SkipLocal);
const auto updateRecentStickers = Api::HasAttachedStickers(media); const auto updateRecentStickers = Api::HasAttachedStickers(media);
const auto starsPaid = std::min(
peer->starsPerMessageChecked(),
options.starsApproved);
if (starsPaid) {
options.starsApproved -= starsPaid;
}
using Flag = MTPmessages_SendMedia::Flag; using Flag = MTPmessages_SendMedia::Flag;
const auto flags = Flag(0) const auto flags = Flag(0)
@ -4371,10 +4319,10 @@ void ApiWrap::sendMediaWithRandomId(
| (options.sendAs ? Flag::f_send_as : Flag(0)) | (options.sendAs ? Flag::f_send_as : Flag(0))
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0)) | (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
| (options.effectId ? Flag::f_effect : Flag(0)) | (options.effectId ? Flag::f_effect : Flag(0))
| (options.invertCaption ? Flag::f_invert_media : Flag(0)) | (options.invertCaption ? Flag::f_invert_media : Flag(0));
| (starsPaid ? Flag::f_allow_paid_stars : Flag(0));
auto &histories = history->owner().histories(); auto &histories = history->owner().histories();
const auto peer = history->peer;
const auto itemId = item->fullId(); const auto itemId = item->fullId();
histories.sendPreparedMessage( histories.sendPreparedMessage(
history, history,
@ -4398,8 +4346,7 @@ void ApiWrap::sendMediaWithRandomId(
MTP_int(options.scheduled), MTP_int(options.scheduled),
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()), (options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, options.shortcutId), Data::ShortcutIdToMTP(_session, options.shortcutId),
MTP_long(options.effectId), MTP_long(options.effectId)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) { ), [=](const MTPUpdates &result, const MTP::Response &response) {
if (done) done(true); if (done) done(true);
if (updateRecentStickers) { if (updateRecentStickers) {
@ -4420,7 +4367,7 @@ void ApiWrap::sendMultiPaidMedia(
Expects(album->options.price > 0); Expects(album->options.price > 0);
const auto groupId = album->groupId; const auto groupId = album->groupId;
auto &options = album->options; const auto &options = album->options;
const auto randomId = album->items.front().randomId; const auto randomId = album->items.front().randomId;
auto medias = album->items | ranges::view::transform([]( auto medias = album->items | ranges::view::transform([](
const SendingAlbum::Item &part) { const SendingAlbum::Item &part) {
@ -4430,7 +4377,6 @@ void ApiWrap::sendMultiPaidMedia(
const auto history = item->history(); const auto history = item->history();
const auto replyTo = item->replyTo(); const auto replyTo = item->replyTo();
const auto peer = history->peer;
auto caption = item->originalText(); auto caption = item->originalText();
TextUtilities::Trim(caption); TextUtilities::Trim(caption);
@ -4438,12 +4384,6 @@ void ApiWrap::sendMultiPaidMedia(
_session, _session,
caption.entities, caption.entities,
Api::ConvertOption::SkipLocal); Api::ConvertOption::SkipLocal);
const auto starsPaid = std::min(
peer->starsPerMessageChecked(),
options.starsApproved);
if (starsPaid) {
options.starsApproved -= starsPaid;
}
using Flag = MTPmessages_SendMedia::Flag; using Flag = MTPmessages_SendMedia::Flag;
const auto flags = Flag(0) const auto flags = Flag(0)
@ -4456,10 +4396,10 @@ void ApiWrap::sendMultiPaidMedia(
| (options.sendAs ? Flag::f_send_as : Flag(0)) | (options.sendAs ? Flag::f_send_as : Flag(0))
| (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0)) | (options.shortcutId ? Flag::f_quick_reply_shortcut : Flag(0))
| (options.effectId ? Flag::f_effect : Flag(0)) | (options.effectId ? Flag::f_effect : Flag(0))
| (options.invertCaption ? Flag::f_invert_media : Flag(0)) | (options.invertCaption ? Flag::f_invert_media : Flag(0));
| (starsPaid ? Flag::f_allow_paid_stars : Flag(0));
auto &histories = history->owner().histories(); auto &histories = history->owner().histories();
const auto peer = history->peer;
const auto itemId = item->fullId(); const auto itemId = item->fullId();
album->sent = true; album->sent = true;
histories.sendPreparedMessage( histories.sendPreparedMessage(
@ -4482,8 +4422,7 @@ void ApiWrap::sendMultiPaidMedia(
MTP_int(options.scheduled), MTP_int(options.scheduled),
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()), (options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, options.shortcutId), Data::ShortcutIdToMTP(_session, options.shortcutId),
MTP_long(options.effectId), MTP_long(options.effectId)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) { ), [=](const MTPUpdates &result, const MTP::Response &response) {
if (const auto album = _sendingAlbums.take(groupId)) { if (const auto album = _sendingAlbums.take(groupId)) {
const auto copy = (*album)->items; const auto copy = (*album)->items;
@ -4579,12 +4518,6 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
const auto history = sample->history(); const auto history = sample->history();
const auto replyTo = sample->replyTo(); const auto replyTo = sample->replyTo();
const auto sendAs = album->options.sendAs; const auto sendAs = album->options.sendAs;
const auto starsPaid = std::min(
history->peer->starsPerMessageChecked() * int(medias.size()),
album->options.starsApproved);
if (starsPaid) {
album->options.starsApproved -= starsPaid;
}
using Flag = MTPmessages_SendMultiMedia::Flag; using Flag = MTPmessages_SendMultiMedia::Flag;
const auto flags = Flag(0) const auto flags = Flag(0)
| (replyTo ? Flag::f_reply_to : Flag(0)) | (replyTo ? Flag::f_reply_to : Flag(0))
@ -4597,8 +4530,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
? Flag::f_quick_reply_shortcut ? Flag::f_quick_reply_shortcut
: Flag(0)) : Flag(0))
| (album->options.effectId ? Flag::f_effect : Flag(0)) | (album->options.effectId ? Flag::f_effect : Flag(0))
| (album->options.invertCaption ? Flag::f_invert_media : Flag(0)) | (album->options.invertCaption ? Flag::f_invert_media : Flag(0));
| (starsPaid ? Flag::f_allow_paid_stars : Flag(0));
auto &histories = history->owner().histories(); auto &histories = history->owner().histories();
const auto peer = history->peer; const auto peer = history->peer;
album->sent = true; album->sent = true;
@ -4614,8 +4546,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
MTP_int(album->options.scheduled), MTP_int(album->options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()), (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, album->options.shortcutId), Data::ShortcutIdToMTP(_session, album->options.shortcutId),
MTP_long(album->options.effectId), MTP_long(album->options.effectId)
MTP_long(starsPaid)
), [=](const MTPUpdates &result, const MTP::Response &response) { ), [=](const MTPUpdates &result, const MTP::Response &response) {
_sendingAlbums.remove(groupId); _sendingAlbums.remove(groupId);

View file

@ -306,7 +306,7 @@ public:
void finishForwarding(const SendAction &action); void finishForwarding(const SendAction &action);
void forwardMessages( void forwardMessages(
Data::ResolvedForwardDraft &&draft, Data::ResolvedForwardDraft &&draft,
SendAction action, const SendAction &action,
FnMut<void()> &&successCallback = nullptr); FnMut<void()> &&successCallback = nullptr);
void shareContact( void shareContact(
const QString &phone, const QString &phone,
@ -368,7 +368,7 @@ public:
void sendInlineResult( void sendInlineResult(
not_null<UserData*> bot, not_null<UserData*> bot,
not_null<InlineBots::Result*> data, not_null<InlineBots::Result*> data,
SendAction action, const SendAction &action,
std::optional<MsgId> localMessageId, std::optional<MsgId> localMessageId,
Fn<void(bool)> done = nullptr); Fn<void(bool)> done = nullptr);
void sendMessageFail( void sendMessageFail(

View file

@ -43,20 +43,18 @@ bool ResolveUser(
return true; return true;
} }
searchById( searchById(userId,
userId, &controller->session(),
&controller->session(), [=](const QString &title, UserData *data)
[=](const QString &title, UserData *data) {
{ if (data) {
if (data) { controller->showPeerInfo(data);
controller->showPeerInfo(data); return;
return; }
}
Core::App().hideMediaView(); Core::App().hideMediaView();
Ui::show(Ui::MakeInformBox(tr::ayu_UserNotFoundMessage())); Ui::show(Ui::MakeInformBox(tr::ayu_UserNotFoundMessage()));
} });
);
return true; return true;
} }

File diff suppressed because it is too large Load diff

View file

@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()]. ** [sqlite_version()] and [sqlite_source_id()].
*/ */
#define SQLITE_VERSION "3.49.1" #define SQLITE_VERSION "3.48.0"
#define SQLITE_VERSION_NUMBER 3049001 #define SQLITE_VERSION_NUMBER 3048000
#define SQLITE_SOURCE_ID "2025-02-18 13:38:58 873d4e274b4988d260ba8354a9718324a1c26187a4ab4c1cc0227c03d0f10e70" #define SQLITE_SOURCE_ID "2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010"
/* /*
** CAPI3REF: Run-Time Library Version Numbers ** CAPI3REF: Run-Time Library Version Numbers
@ -2211,15 +2211,7 @@ struct sqlite3_mem_methods {
** CAPI3REF: Database Connection Configuration Options ** CAPI3REF: Database Connection Configuration Options
** **
** These constants are the available integer configuration options that ** These constants are the available integer configuration options that
** can be passed as the second parameter to the [sqlite3_db_config()] interface. ** can be passed as the second argument to the [sqlite3_db_config()] interface.
**
** The [sqlite3_db_config()] interface is a var-args functions. It takes a
** variable number of parameters, though always at least two. The number of
** parameters passed into sqlite3_db_config() depends on which of these
** constants is given as the second parameter. This documentation page
** refers to parameters beyond the second as "arguments". Thus, when this
** page says "the N-th argument" it means "the N-th parameter past the
** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()".
** **
** New configuration options may be added in future releases of SQLite. ** New configuration options may be added in future releases of SQLite.
** Existing configuration options might be discontinued. Applications ** Existing configuration options might be discontinued. Applications
@ -2231,14 +2223,8 @@ struct sqlite3_mem_methods {
** <dl> ** <dl>
** [[SQLITE_DBCONFIG_LOOKASIDE]] ** [[SQLITE_DBCONFIG_LOOKASIDE]]
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt> ** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the ** <dd> ^This option takes three additional arguments that determine the
** configuration of the lookaside memory allocator within a database ** [lookaside memory allocator] configuration for the [database connection].
** connection.
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
** in the [DBCONFIG arguments|usual format].
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
** should have a total of five parameters.
** ^The first argument (the third parameter to [sqlite3_db_config()] is a ** ^The first argument (the third parameter to [sqlite3_db_config()] is a
** pointer to a memory buffer to use for lookaside memory. ** pointer to a memory buffer to use for lookaside memory.
** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb ** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
@ -2261,8 +2247,7 @@ struct sqlite3_mem_methods {
** [[SQLITE_DBCONFIG_ENABLE_FKEY]] ** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt> ** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
** <dd> ^This option is used to enable or disable the enforcement of ** <dd> ^This option is used to enable or disable the enforcement of
** [foreign key constraints]. This is the same setting that is ** [foreign key constraints]. There should be two additional arguments.
** enabled or disabled by the [PRAGMA foreign_keys] statement.
** The first argument is an integer which is 0 to disable FK enforcement, ** The first argument is an integer which is 0 to disable FK enforcement,
** positive to enable FK enforcement or negative to leave FK enforcement ** positive to enable FK enforcement or negative to leave FK enforcement
** unchanged. The second parameter is a pointer to an integer into which ** unchanged. The second parameter is a pointer to an integer into which
@ -2284,13 +2269,13 @@ struct sqlite3_mem_methods {
** <p>Originally this option disabled all triggers. ^(However, since ** <p>Originally this option disabled all triggers. ^(However, since
** SQLite version 3.35.0, TEMP triggers are still allowed even if ** SQLite version 3.35.0, TEMP triggers are still allowed even if
** this option is off. So, in other words, this option now only disables ** this option is off. So, in other words, this option now only disables
** triggers in the main database schema or in the schemas of [ATTACH]-ed ** triggers in the main database schema or in the schemas of ATTACH-ed
** databases.)^ </dd> ** databases.)^ </dd>
** **
** [[SQLITE_DBCONFIG_ENABLE_VIEW]] ** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt> ** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
** <dd> ^This option is used to enable or disable [CREATE VIEW | views]. ** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
** There must be two additional arguments. ** There should be two additional arguments.
** The first argument is an integer which is 0 to disable views, ** The first argument is an integer which is 0 to disable views,
** positive to enable views or negative to leave the setting unchanged. ** positive to enable views or negative to leave the setting unchanged.
** The second parameter is a pointer to an integer into which ** The second parameter is a pointer to an integer into which
@ -2309,7 +2294,7 @@ struct sqlite3_mem_methods {
** <dd> ^This option is used to enable or disable the ** <dd> ^This option is used to enable or disable the
** [fts3_tokenizer()] function which is part of the ** [fts3_tokenizer()] function which is part of the
** [FTS3] full-text search engine extension. ** [FTS3] full-text search engine extension.
** There must be two additional arguments. ** There should be two additional arguments.
** The first argument is an integer which is 0 to disable fts3_tokenizer() or ** The first argument is an integer which is 0 to disable fts3_tokenizer() or
** positive to enable fts3_tokenizer() or negative to leave the setting ** positive to enable fts3_tokenizer() or negative to leave the setting
** unchanged. ** unchanged.
@ -2324,7 +2309,7 @@ struct sqlite3_mem_methods {
** interface independently of the [load_extension()] SQL function. ** interface independently of the [load_extension()] SQL function.
** The [sqlite3_enable_load_extension()] API enables or disables both the ** The [sqlite3_enable_load_extension()] API enables or disables both the
** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()].
** There must be two additional arguments. ** There should be two additional arguments.
** When the first argument to this interface is 1, then only the C-API is ** When the first argument to this interface is 1, then only the C-API is
** enabled and the SQL function remains disabled. If the first argument to ** enabled and the SQL function remains disabled. If the first argument to
** this interface is 0, then both the C-API and the SQL function are disabled. ** this interface is 0, then both the C-API and the SQL function are disabled.
@ -2338,30 +2323,23 @@ struct sqlite3_mem_methods {
** **
** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt> ** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
** <dd> ^This option is used to change the name of the "main" database ** <dd> ^This option is used to change the name of the "main" database
** schema. This option does not follow the ** schema. ^The sole argument is a pointer to a constant UTF8 string
** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format]. ** which will become the new schema name in place of "main". ^SQLite
** This option takes exactly one additional argument so that the ** does not make a copy of the new main schema name string, so the application
** [sqlite3_db_config()] call has a total of three parameters. The ** must ensure that the argument passed into this DBCONFIG option is unchanged
** extra argument must be a pointer to a constant UTF8 string which ** until after the database connection closes.
** will become the new schema name in place of "main". ^SQLite does
** not make a copy of the new main schema name string, so the application
** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME
** is unchanged until after the database connection closes.
** </dd> ** </dd>
** **
** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] ** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt> ** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
** <dd> Usually, when a database in [WAL mode] is closed or detached from a ** <dd> Usually, when a database in wal mode is closed or detached from a
** database handle, SQLite checks if if there are other connections to the ** database handle, SQLite checks if this will mean that there are now no
** same database, and if there are no other database connection (if the ** connections at all to the database. If so, it performs a checkpoint
** connection being closed is the last open connection to the database), ** operation before closing the connection. This option may be used to
** then SQLite performs a [checkpoint] before closing the connection and ** override this behavior. The first parameter passed to this operation
** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can ** is an integer - positive to disable checkpoints-on-close, or zero (the
** be used to override that behavior. The first argument passed to this ** default) to enable them, and negative to leave the setting unchanged.
** operation (the third parameter to [sqlite3_db_config()]) is an integer ** The second parameter is a pointer to an integer
** which is positive to disable checkpoints-on-close, or zero (the default)
** to enable them, and negative to leave the setting unchanged.
** The second argument (the fourth parameter) is a pointer to an integer
** into which is written 0 or 1 to indicate whether checkpoints-on-close ** into which is written 0 or 1 to indicate whether checkpoints-on-close
** have been disabled - 0 if they are not disabled, 1 if they are. ** have been disabled - 0 if they are not disabled, 1 if they are.
** </dd> ** </dd>
@ -2522,7 +2500,7 @@ struct sqlite3_mem_methods {
** statistics. For statistics to be collected, the flag must be set on ** statistics. For statistics to be collected, the flag must be set on
** the database handle both when the SQL statement is prepared and when it ** the database handle both when the SQL statement is prepared and when it
** is stepped. The flag is set (collection of statistics is enabled) ** is stepped. The flag is set (collection of statistics is enabled)
** by default. <p>This option takes two arguments: an integer and a pointer to ** by default. This option takes two arguments: an integer and a pointer to
** an integer.. The first argument is 1, 0, or -1 to enable, disable, or ** an integer.. The first argument is 1, 0, or -1 to enable, disable, or
** leave unchanged the statement scanstatus option. If the second argument ** leave unchanged the statement scanstatus option. If the second argument
** is not NULL, then the value of the statement scanstatus setting after ** is not NULL, then the value of the statement scanstatus setting after
@ -2536,7 +2514,7 @@ struct sqlite3_mem_methods {
** in which tables and indexes are scanned so that the scans start at the end ** in which tables and indexes are scanned so that the scans start at the end
** and work toward the beginning rather than starting at the beginning and ** and work toward the beginning rather than starting at the beginning and
** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the ** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the
** same as setting [PRAGMA reverse_unordered_selects]. <p>This option takes ** same as setting [PRAGMA reverse_unordered_selects]. This option takes
** two arguments which are an integer and a pointer to an integer. The first ** two arguments which are an integer and a pointer to an integer. The first
** argument is 1, 0, or -1 to enable, disable, or leave unchanged the ** argument is 1, 0, or -1 to enable, disable, or leave unchanged the
** reverse scan order flag, respectively. If the second argument is not NULL, ** reverse scan order flag, respectively. If the second argument is not NULL,
@ -2545,76 +2523,7 @@ struct sqlite3_mem_methods {
** first argument. ** first argument.
** </dd> ** </dd>
** **
** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]]
** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE</dt>
** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables
** the ability of the [ATTACH DATABASE] SQL command to create a new database
** file if the database filed named in the ATTACH command does not already
** exist. This ability of ATTACH to create a new database is enabled by
** default. Applications can disable or reenable the ability for ATTACH to
** create new database files using this DBCONFIG option.<p>
** This option takes two arguments which are an integer and a pointer
** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
** leave unchanged the attach-create flag, respectively. If the second
** argument is not NULL, then 0 or 1 is written into the integer that the
** second argument points to depending on if the attach-create flag is set
** after processing the first argument.
** </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]]
** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE</dt>
** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the
** ability of the [ATTACH DATABASE] SQL command to open a database for writing.
** This capability is enabled by default. Applications can disable or
** reenable this capability using the current DBCONFIG option. If the
** the this capability is disabled, the [ATTACH] command will still work,
** but the database will be opened read-only. If this option is disabled,
** then the ability to create a new database using [ATTACH] is also disabled,
** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]
** option.<p>
** This option takes two arguments which are an integer and a pointer
** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
** leave unchanged the ability to ATTACH another database for writing,
** respectively. If the second argument is not NULL, then 0 or 1 is written
** into the integer to which the second argument points, depending on whether
** the ability to ATTACH a read/write database is enabled or disabled
** after processing the first argument.
** </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]]
** <dt>SQLITE_DBCONFIG_ENABLE_COMMENTS</dt>
** <dd>The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the
** ability to include comments in SQL text. Comments are enabled by default.
** An application can disable or reenable comments in SQL text using this
** DBCONFIG option.<p>
** This option takes two arguments which are an integer and a pointer
** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
** leave unchanged the ability to use comments in SQL text,
** respectively. If the second argument is not NULL, then 0 or 1 is written
** into the integer that the second argument points to depending on if
** comments are allowed in SQL text after processing the first argument.
** </dd>
**
** </dl> ** </dl>
**
** [[DBCONFIG arguments]] <h3>Arguments To SQLITE_DBCONFIG Options</h3>
**
** <p>Most of the SQLITE_DBCONFIG options take two arguments, so that the
** overall call to [sqlite3_db_config()] has a total of four parameters.
** The first argument (the third parameter to sqlite3_db_config()) is a integer.
** The second argument is a pointer to an integer. If the first argument is 1,
** then the option becomes enabled. If the first integer argument is 0, then the
** option is disabled. If the first argument is -1, then the option setting
** is unchanged. The second argument, the pointer to an integer, may be NULL.
** If the second argument is not NULL, then a value of 0 or 1 is written into
** the integer to which the second argument points, depending on whether the
** setting is disabled or enabled after applying any changes specified by
** the first argument.
**
** <p>While most SQLITE_DBCONFIG options use the argument format
** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME]
** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the
** documentation of those exceptional options for details.
*/ */
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
@ -2636,10 +2545,7 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */
#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ #define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */
#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */ #define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */
#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */
#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */
/* /*
** CAPI3REF: Enable Or Disable Extended Result Codes ** CAPI3REF: Enable Or Disable Extended Result Codes
@ -10842,9 +10748,8 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
/* /*
** CAPI3REF: Serialize a database ** CAPI3REF: Serialize a database
** **
** The sqlite3_serialize(D,S,P,F) interface returns a pointer to ** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory
** memory that is a serialization of the S database on ** that is a serialization of the S database on [database connection] D.
** [database connection] D. If S is a NULL pointer, the main database is used.
** If P is not a NULL pointer, then the size of the database in bytes ** If P is not a NULL pointer, then the size of the database in bytes
** is written into *P. ** is written into *P.
** **

File diff suppressed because it is too large Load diff

View file

@ -34,7 +34,6 @@
#include "data/data_session.h" #include "data/data_session.h"
#include "history/view/history_view_context_menu.h" #include "history/view/history_view_context_menu.h"
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
namespace AyuUi { namespace AyuUi {
@ -59,14 +58,13 @@ void AddDeletedMessagesActions(PeerData *peerData,
return; return;
} }
addCallback( addCallback(tr::ayu_ViewDeletedMenuText(tr::now),
tr::ayu_ViewDeletedMenuText(tr::now), [=]
[=] {
{ sessionController->session().tryResolveWindow()
sessionController->session().tryResolveWindow() ->showSection(std::make_shared<MessageHistory::SectionMemento>(peerData, nullptr, topicId));
->showSection(std::make_shared<MessageHistory::SectionMemento>(peerData, nullptr, topicId)); },
}, &st::menuIconArchive);
&st::menuIconArchive);
} }
void AddJumpToBeginningAction(PeerData *peerData, void AddJumpToBeginningAction(PeerData *peerData,
@ -149,38 +147,16 @@ void AddJumpToBeginningAction(PeerData *peerData,
&st::ayuMenuIconToBeginning); &st::ayuMenuIconToBeginning);
} }
void AddOpenChannelAction(PeerData *peerData,
not_null<Window::SessionController*> sessionController,
const Window::PeerMenuCallback &addCallback) {
if (!peerData || !peerData->isMegagroup()) {
return;
}
const auto chat = peerData->asMegagroup()->linkedChat();
if (!chat) {
return;
}
addCallback(
tr::lng_context_open_channel(tr::now),
[=]
{
sessionController->showPeerHistory(chat, Window::SectionShow::Way::Forward);
},
&st::menuIconChannel);
}
void AddHistoryAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) { void AddHistoryAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
if (AyuMessages::hasRevisions(item)) { if (AyuMessages::hasRevisions(item)) {
menu->addAction( menu->addAction(tr::ayu_EditsHistoryMenuText(tr::now),
tr::ayu_EditsHistoryMenuText(tr::now), [=]
[=] {
{ item->history()->session().tryResolveWindow()
item->history()->session().tryResolveWindow() ->showSection(
->showSection( std::make_shared<MessageHistory::SectionMemento>(item->history()->peer, item, 0));
std::make_shared<MessageHistory::SectionMemento>(item->history()->peer, item, 0)); },
}, &st::ayuEditsHistoryIcon);
&st::ayuEditsHistoryIcon);
} }
} }
@ -195,16 +171,15 @@ void AddHideMessageAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
} }
const auto history = item->history(); const auto history = item->history();
menu->addAction( menu->addAction(tr::ayu_ContextHideMessage(tr::now),
tr::ayu_ContextHideMessage(tr::now), [=]()
[=]() {
{ item->destroy();
item->destroy(); history->requestChatListMessage();
history->requestChatListMessage();
AyuState::hide(item); AyuState::hide(item);
}, },
&st::menuIconClear); &st::menuIconClear);
} }
void AddUserMessagesAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) { void AddUserMessagesAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
@ -457,77 +432,31 @@ void AddReadUntilAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) {
return; return;
} }
menu->addAction( menu->addAction(tr::ayu_ReadUntilMenuText(tr::now),
tr::ayu_ReadUntilMenuText(tr::now), [=]()
[=]()
{
readHistory(item);
if (item->media() && item->media()->ttlSeconds() <= 0 && item->unsupportedTTL() <= 0 && !item->out() && item
->isUnreadMedia()) {
const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id));
if (const auto channel = item->history()->peer->asChannel()) {
item->history()->session().api().request(MTPchannels_ReadMessageContents(
channel->inputChannel,
ids
)).send();
} else {
item->history()->session().api().request(MTPmessages_ReadMessageContents(
ids
)).done([=](const MTPmessages_AffectedMessages &result)
{ {
item->history()->session().api().applyAffectedMessages( readHistory(item);
item->history()->peer, if (item->media() && !item->media()->ttlSeconds()) {
result); const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id));
}).send(); if (const auto channel = item->history()->peer->asChannel()) {
} item->history()->session().api().request(MTPchannels_ReadMessageContents(
item->markContentsRead(); channel->inputChannel,
} ids
}, )).send();
&st::menuIconShowInChat); } else {
} item->history()->session().api().request(MTPmessages_ReadMessageContents(
ids
void AddBurnAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item) { )).done([=](const MTPmessages_AffectedMessages &result)
if (!item->media() || item->media()->ttlSeconds() <= 0 && item->unsupportedTTL() <= 0 || item->out() || {
!item->isUnreadMedia()) { item->history()->session().api().applyAffectedMessages(
return; item->history()->peer,
} result);
}).send();
menu->addAction( }
tr::ayu_ExpireMediaContextMenuText(tr::now), item->markContentsRead();
[=]() }
{ },
const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id)); &st::menuIconShowInChat);
const auto callback = [=]()
{
if (const auto window = Core::App().activeWindow()) {
if (const auto controller = window->sessionController()) {
controller->showToast(tr::lng_box_ok(tr::now));
}
}
};
if (const auto channel = item->history()->peer->asChannel()) {
item->history()->session().api().request(MTPchannels_ReadMessageContents(
channel->inputChannel,
ids
)).done([=]()
{
callback();
}).send();
} else {
item->history()->session().api().request(MTPmessages_ReadMessageContents(
ids
)).done([=](const MTPmessages_AffectedMessages &result)
{
item->history()->session().api().applyAffectedMessages(
item->history()->peer,
result);
callback();
}).send();
}
item->markContentsRead();
},
&st::menuIconTTLAny);
} }
} // namespace AyuUi } // namespace AyuUi

View file

@ -25,15 +25,10 @@ void AddJumpToBeginningAction(PeerData *peerData,
not_null<Window::SessionController*> sessionController, not_null<Window::SessionController*> sessionController,
const Window::PeerMenuCallback &addCallback); const Window::PeerMenuCallback &addCallback);
void AddOpenChannelAction(PeerData *peerData,
not_null<Window::SessionController*> sessionController,
const Window::PeerMenuCallback &addCallback);
void AddHistoryAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item); void AddHistoryAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
void AddHideMessageAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item); void AddHideMessageAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
void AddUserMessagesAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item); void AddUserMessagesAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
void AddMessageDetailsAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item); void AddMessageDetailsAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
void AddReadUntilAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item); void AddReadUntilAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
void AddBurnAction(not_null<Ui::PopupMenu*> menu, HistoryItem *item);
} }

View file

@ -218,73 +218,59 @@ ActionStickerPackAuthor::ActionStickerPackAuthor(not_null<Menu::Menu*> menu,
} }
void ActionStickerPackAuthor::searchAuthor(ID authorId) { void ActionStickerPackAuthor::searchAuthor(ID authorId) {
const auto session = _session; const auto pointer = Ui::MakeWeak(this);
const auto weak = Ui::MakeWeak(this); searchById(authorId,
_session,
[=](const QString &username, UserData *user)
{
if (!pointer) {
LOG(("ContextActionStickerAuthor: searchById callback after destruction"));
return;
}
if (username.isEmpty() && !user) {
_subText = QString(tr::ayu_MessageDetailsPackOwnerNotFoundPC(tr::now));
setClickedCallback(
[=]
{
QGuiApplication::clipboard()->setText(QString::number(authorId));
if (const auto window = _session->tryResolveWindow()) {
if (const auto mainWidget = window->widget()->sessionController()) {
mainWidget->showToast(tr::ayu_IDCopiedToast(tr::now));
}
}
});
searchById( crl::on_main(
authorId, [=]
session, {
[session, weak, authorId](const QString &username, UserData *user) update();
{ });
if (!weak) { return;
LOG(("ContextActionStickerAuthor: searchById callback after destruction")); }
return;
}
const auto strong = weak.data(); const auto title = username.isEmpty() ? user ? user->name() : QString() : username;
if (!strong) { const auto callback = [=]
LOG(("ContextActionStickerAuthor: weak.data() returned null")); {
return; if (user) {
} if (const auto window = _session->tryResolveWindow()) {
if (const auto mainWidget = window->widget()->sessionController()) {
mainWidget->showPeer(user);
}
}
} else {
QGuiApplication::clipboard()->setText(title);
}
};
if (username.isEmpty() && !user) { setClickedCallback(callback);
strong->_subText = QString(tr::ayu_MessageDetailsPackOwnerNotFoundPC(tr::now));
strong->setClickedCallback(
[authorId, session]
{
QGuiApplication::clipboard()->setText(QString::number(authorId));
if (const auto window = session->tryResolveWindow()) {
if (const auto mainWidget = window->widget()->sessionController()) {
mainWidget->showToast(tr::ayu_IDCopiedToast(tr::now));
}
}
});
crl::on_main( _subText = QString(title);
[weak] crl::on_main(
{ [=]
if (const auto strongInner = weak.data()) { {
strongInner->update(); update();
} });
}); });
return;
}
const auto title = username.isEmpty() ? (user ? user->name() : QString()) : username;
const auto callback = [user, title, session]
{
if (user) {
if (const auto window = session->tryResolveWindow()) {
if (const auto mainWidget = window->widget()->sessionController()) {
mainWidget->showPeer(user);
}
}
} else {
QGuiApplication::clipboard()->setText(title);
}
};
strong->setClickedCallback(callback);
strong->_subText = QString(title);
crl::on_main(
[weak]
{
if (const auto strongInner = weak.data()) {
strongInner->update();
}
});
}
);
} }
} // namespace } // namespace

View file

@ -1078,9 +1078,7 @@ void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
? tr::lng_background_other_group(tr::now) ? tr::lng_background_other_group(tr::now)
: forChannel() : forChannel()
? tr::lng_background_other_channel(tr::now) ? tr::lng_background_other_channel(tr::now)
: (_forPeer : (_forPeer && !_fromMessageId)
&& !_fromMessageId
&& !_forPeer->starsPerMessageChecked())
? tr::lng_background_other_info( ? tr::lng_background_other_info(
tr::now, tr::now,
lt_user, lt_user,

View file

@ -1122,10 +1122,3 @@ profileQrBackgroundRadius: 12px;
profileQrIcon: icon{{ "qr_mini", windowActiveTextFg }}; profileQrIcon: icon{{ "qr_mini", windowActiveTextFg }};
profileQrBackgroundMargins: margins(36px, 12px, 36px, 12px); profileQrBackgroundMargins: margins(36px, 12px, 36px, 12px);
profileQrBackgroundPadding: margins(0px, 24px, 0px, 24px); profileQrBackgroundPadding: margins(0px, 24px, 0px, 24px);
foldersMenu: PopupMenu(popupMenuWithIcons) {
maxHeight: 320px;
menu: Menu(menuWithIcons) {
itemPadding: margins(54px, 8px, 44px, 8px);
}
}

View file

@ -172,6 +172,13 @@ void ChangeFilterById(
const auto account = not_null(&history->session().account()); const auto account = not_null(&history->session().account());
if (const auto controller = Core::App().windowFor(account)) { if (const auto controller = Core::App().windowFor(account)) {
const auto isStatic = name.isStatic; const auto isStatic = name.isStatic;
const auto textContext = [=](not_null<QWidget*> widget) {
return Core::MarkedTextContext{
.session = &history->session(),
.customEmojiRepaint = [=] { widget->update(); },
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
controller->showToast({ controller->showToast({
.text = (add .text = (add
? tr::lng_filters_toast_add ? tr::lng_filters_toast_add
@ -182,10 +189,7 @@ void ChangeFilterById(
lt_folder, lt_folder,
Ui::Text::Wrapped(name.text, EntityType::Bold), Ui::Text::Wrapped(name.text, EntityType::Bold),
Ui::Text::WithEntities), Ui::Text::WithEntities),
.textContext = Core::TextContext({ .textContext = textContext,
.session = &history->session(),
.customEmojiLoopLimit = isStatic ? -1 : 0,
}),
}); });
} }
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {
@ -286,17 +290,19 @@ void FillChooseFilterMenu(
const auto title = filter.title(); const auto title = filter.title();
auto item = base::make_unique_q<FilterAction>( auto item = base::make_unique_q<FilterAction>(
menu.get(), menu.get(),
menu->st().menu, st::foldersMenu,
Ui::Menu::CreateAction( Ui::Menu::CreateAction(
menu.get(), menu.get(),
Ui::Text::FixAmpersandInAction(title.text.text), Ui::Text::FixAmpersandInAction(title.text.text),
std::move(callback)), std::move(callback)),
contains ? &st::mediaPlayerMenuCheck : nullptr, contains ? &st::mediaPlayerMenuCheck : nullptr,
contains ? &st::mediaPlayerMenuCheck : nullptr); contains ? &st::mediaPlayerMenuCheck : nullptr);
item->setMarkedText(title.text, QString(), Core::TextContext({ const auto context = Core::MarkedTextContext{
.session = &history->session(), .session = &history->session(),
.customEmojiRepaint = [raw = item.get()] { raw->update(); },
.customEmojiLoopLimit = title.isStatic ? -1 : 0, .customEmojiLoopLimit = title.isStatic ? -1 : 0,
})); };
item->setMarkedText(title.text, QString(), context);
item->setIcon(Icon(showColors ? filter : filter.withColorIndex({}))); item->setIcon(Icon(showColors ? filter : filter.withColorIndex({})));
const auto action = menu->addAction(std::move(item)); const auto action = menu->addAction(std::move(item));

View file

@ -817,15 +817,13 @@ CreatePollBox::CreatePollBox(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
PollData::Flags chosen, PollData::Flags chosen,
PollData::Flags disabled, PollData::Flags disabled,
rpl::producer<int> starsRequired,
Api::SendType sendType, Api::SendType sendType,
SendMenu::Details sendMenuDetails) SendMenu::Details sendMenuDetails)
: _controller(controller) : _controller(controller)
, _chosen(chosen) , _chosen(chosen)
, _disabled(disabled) , _disabled(disabled)
, _sendType(sendType) , _sendType(sendType)
, _sendMenuDetails([result = sendMenuDetails] { return result; }) , _sendMenuDetails([result = sendMenuDetails] { return result; }) {
, _starsRequired(std::move(starsRequired)) {
} }
rpl::producer<CreatePollBox::Result> CreatePollBox::submitRequests() const { rpl::producer<CreatePollBox::Result> CreatePollBox::submitRequests() const {
@ -1228,11 +1226,10 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
_sendMenuDetails()); _sendMenuDetails());
}; };
const auto submit = addButton( const auto submit = addButton(
tr::lng_polls_create_button(), (isNormal
? tr::lng_polls_create_button()
: tr::lng_schedule_button()),
[=] { isNormal ? send({}) : schedule(); }); [=] { isNormal ? send({}) : schedule(); });
submit->setText(PaidSendButtonText(_starsRequired.value(), isNormal
? tr::lng_polls_create_button()
: tr::lng_schedule_button()));
const auto sendMenuDetails = [=] { const auto sendMenuDetails = [=] {
collectError(); collectError();
return (*error) ? SendMenu::Details() : _sendMenuDetails(); return (*error) ? SendMenu::Details() : _sendMenuDetails();

View file

@ -42,7 +42,6 @@ public:
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
PollData::Flags chosen, PollData::Flags chosen,
PollData::Flags disabled, PollData::Flags disabled,
rpl::producer<int> starsRequired,
Api::SendType sendType, Api::SendType sendType,
SendMenu::Details sendMenuDetails); SendMenu::Details sendMenuDetails);
@ -77,7 +76,6 @@ private:
const PollData::Flags _disabled = PollData::Flags(); const PollData::Flags _disabled = PollData::Flags();
const Api::SendType _sendType = Api::SendType(); const Api::SendType _sendType = Api::SendType();
const Fn<SendMenu::Details()> _sendMenuDetails; const Fn<SendMenu::Details()> _sendMenuDetails;
rpl::variable<int> _starsRequired;
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel; base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
Fn<void()> _setInnerFocus; Fn<void()> _setInnerFocus;
Fn<rpl::producer<bool>()> _dataIsValidValue; Fn<rpl::producer<bool>()> _dataIsValidValue;

View file

@ -12,9 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/premium_graphics.h" #include "ui/effects/premium_graphics.h"
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
#include "ui/widgets/checkbox.h" #include "ui/widgets/checkbox.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.h"
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/wrap/slide_wrap.h" #include "ui/wrap/slide_wrap.h"
@ -23,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "settings/settings_premium.h" #include "settings/settings_premium.h"
#include "settings/settings_privacy_controllers.h"
#include "settings/settings_privacy_security.h" #include "settings/settings_privacy_security.h"
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
@ -45,8 +42,6 @@ namespace {
constexpr auto kPremiumsRowId = PeerId(FakeChatId(BareId(1))).value; constexpr auto kPremiumsRowId = PeerId(FakeChatId(BareId(1))).value;
constexpr auto kMiniAppsRowId = PeerId(FakeChatId(BareId(2))).value; constexpr auto kMiniAppsRowId = PeerId(FakeChatId(BareId(2))).value;
constexpr auto kStarsMin = 1;
constexpr auto kDefaultChargeStars = 10;
using Exceptions = Api::UserPrivacy::Exceptions; using Exceptions = Api::UserPrivacy::Exceptions;
@ -457,143 +452,6 @@ auto PrivacyExceptionsBoxController::createRow(not_null<History*> history)
return result; return result;
} }
[[nodiscard]] object_ptr<Ui::RpWidget> MakeChargeStarsSlider(
QWidget *parent,
not_null<const style::MediaSlider*> sliderStyle,
not_null<const style::FlatLabel*> labelStyle,
int valuesCount,
Fn<int(int)> valueByIndex,
int value,
int maxValue,
Fn<void(int)> valueProgress,
Fn<void(int)> valueFinished) {
auto result = object_ptr<Ui::VerticalLayout>(parent);
const auto raw = result.data();
const auto labels = raw->add(object_ptr<Ui::RpWidget>(raw));
const auto min = Ui::CreateChild<Ui::FlatLabel>(
raw,
QString::number(kStarsMin),
*labelStyle);
const auto max = Ui::CreateChild<Ui::FlatLabel>(
raw,
QString::number(maxValue),
*labelStyle);
const auto current = Ui::CreateChild<Ui::FlatLabel>(
raw,
QString::number(value),
*labelStyle);
min->setTextColorOverride(st::windowSubTextFg->c);
max->setTextColorOverride(st::windowSubTextFg->c);
const auto slider = raw->add(object_ptr<Ui::MediaSliderWheelless>(
raw,
*sliderStyle));
labels->resize(
labels->width(),
current->height() + st::defaultVerticalListSkip);
struct State {
int indexMin = 0;
int index = 0;
};
const auto state = raw->lifetime().make_state<State>();
const auto updateByIndex = [=] {
const auto outer = labels->width();
const auto minWidth = min->width();
const auto maxWidth = max->width();
const auto currentWidth = current->width();
if (minWidth + maxWidth + currentWidth > outer) {
return;
}
min->moveToLeft(0, 0, outer);
max->moveToRight(0, 0, outer);
current->moveToLeft((outer - current->width()) / 2, 0, outer);
};
const auto updateByValue = [=](int value) {
current->setText(
tr::lng_action_gift_for_stars(tr::now, lt_count, value));
state->index = 0;
auto maxIndex = valuesCount - 1;
while (state->index < maxIndex) {
const auto mid = (state->index + maxIndex) / 2;
const auto midValue = valueByIndex(mid);
if (midValue == value) {
state->index = mid;
break;
} else if (midValue < value) {
state->index = mid + 1;
} else {
maxIndex = mid - 1;
}
}
updateByIndex();
};
const auto progress = [=](int value) {
updateByValue(value);
valueProgress(value);
};
const auto finished = [=](int value) {
updateByValue(value);
valueFinished(value);
};
style::PaletteChanged() | rpl::start_with_next([=] {
min->setTextColorOverride(st::windowSubTextFg->c);
max->setTextColorOverride(st::windowSubTextFg->c);
}, raw->lifetime());
updateByValue(value);
state->indexMin = 0;
slider->setPseudoDiscrete(
valuesCount,
valueByIndex,
value,
progress,
finished,
state->indexMin);
slider->resize(slider->width(), sliderStyle->seekSize.height());
raw->widthValue() | rpl::start_with_next([=](int width) {
labels->resizeToWidth(width);
updateByIndex();
}, slider->lifetime());
return result;
}
void EditNoPaidMessagesExceptions(
not_null<Window::SessionController*> window,
const Api::UserPrivacy::Rule &value) {
auto controller = std::make_unique<PrivacyExceptionsBoxController>(
&window->session(),
tr::lng_messages_privacy_remove_fee(),
value.always,
std::optional<SpecialRowType>());
auto initBox = [=, controller = controller.get()](
not_null<PeerListBox*> box) {
box->addButton(tr::lng_settings_save(), [=] {
auto copy = value;
auto &setTo = copy.always;
setTo.peers = box->collectSelectedRows();
setTo.premiums = false;
setTo.miniapps = false;
auto &removeFrom = copy.never;
for (const auto peer : setTo.peers) {
removeFrom.peers.erase(
ranges::remove(removeFrom.peers, peer),
end(removeFrom.peers));
}
window->session().api().userPrivacy().save(
Api::UserPrivacy::Key::NoPaidMessages,
copy);
box->closeBox();
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
};
window->show(
Box<PeerListBox>(std::move(controller), std::move(initBox)));
}
} // namespace } // namespace
bool EditPrivacyController::hasOption(Option option) const { bool EditPrivacyController::hasOption(Option option) const {
@ -954,27 +812,19 @@ void EditMessagesPrivacyBox(
constexpr auto kOptionAll = 0; constexpr auto kOptionAll = 0;
constexpr auto kOptionPremium = 1; constexpr auto kOptionPremium = 1;
constexpr auto kOptionCharge = 2;
const auto session = &controller->session();
const auto allowed = [=] { const auto allowed = [=] {
return session->premium() return controller->session().premium()
|| session->appConfig().newRequirePremiumFree(); || controller->session().appConfig().newRequirePremiumFree();
}; };
const auto privacy = &session->api().globalPrivacy(); const auto privacy = &controller->session().api().globalPrivacy();
const auto inner = box->verticalLayout(); const auto inner = box->verticalLayout();
inner->add(object_ptr<Ui::PlainShadow>(box)); inner->add(object_ptr<Ui::PlainShadow>(box));
Ui::AddSkip(inner, st::messagePrivacyTopSkip); Ui::AddSkip(inner, st::messagePrivacyTopSkip);
Ui::AddSubsectionTitle(inner, tr::lng_messages_privacy_subtitle()); Ui::AddSubsectionTitle(inner, tr::lng_messages_privacy_subtitle());
const auto group = std::make_shared<Ui::RadiobuttonGroup>( const auto group = std::make_shared<Ui::RadiobuttonGroup>(
(!allowed() privacy->newRequirePremiumCurrent() ? kOptionPremium : kOptionAll);
? kOptionAll
: privacy->newRequirePremiumCurrent()
? kOptionPremium
: privacy->newChargeStarsCurrent()
? kOptionCharge
: kOptionAll));
inner->add( inner->add(
object_ptr<Ui::Radiobutton>( object_ptr<Ui::Radiobutton>(
inner, inner,
@ -996,92 +846,6 @@ void EditMessagesPrivacyBox(
0, 0,
st::messagePrivacyBottomSkip)); st::messagePrivacyBottomSkip));
Ui::AddDividerText(inner, tr::lng_messages_privacy_about());
const auto available = session->appConfig().paidMessagesAvailable();
const auto charged = available
? inner->add(
object_ptr<Ui::Radiobutton>(
inner,
group,
kOptionCharge,
tr::lng_messages_privacy_charge(tr::now),
st::messagePrivacyCheck),
st::settingsSendTypePadding + style::margins(
0,
st::messagePrivacyBottomSkip,
0,
st::messagePrivacyBottomSkip))
: nullptr;
struct State {
rpl::variable<int> stars;
};
const auto state = std::make_shared<State>();
const auto savedValue = privacy->newChargeStarsCurrent();
if (available) {
Ui::AddDividerText(inner, tr::lng_messages_privacy_charge_about());
const auto chargeWrap = inner->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
inner,
object_ptr<Ui::VerticalLayout>(inner)));
const auto chargeInner = chargeWrap->entity();
Ui::AddSkip(chargeInner);
state->stars = SetupChargeSlider(
chargeInner,
session->user(),
savedValue);
Ui::AddSkip(chargeInner);
Ui::AddSubsectionTitle(
chargeInner,
tr::lng_messages_privacy_exceptions());
const auto key = Api::UserPrivacy::Key::NoPaidMessages;
session->api().userPrivacy().reload(key);
auto label = session->api().userPrivacy().value(
key
) | rpl::map([=](const Api::UserPrivacy::Rule &value) {
using namespace Settings;
const auto always = ExceptionUsersCount(value.always.peers);
return always
? tr::lng_edit_privacy_exceptions_count(
tr::now,
lt_count,
always)
: QString();
});
const auto exceptions = Settings::AddButtonWithLabel(
chargeInner,
tr::lng_messages_privacy_remove_fee(),
std::move(label),
st::settingsButtonNoIcon);
const auto shower = exceptions->lifetime().make_state<rpl::lifetime>();
exceptions->setClickedCallback([=] {
*shower = session->api().userPrivacy().value(
key
) | rpl::take(
1
) | rpl::start_with_next([=](const Api::UserPrivacy::Rule &value) {
EditNoPaidMessagesExceptions(controller, value);
});
});
Ui::AddSkip(chargeInner);
Ui::AddDividerText(
chargeInner,
tr::lng_messages_privacy_remove_about());
using namespace rpl::mappers;
chargeWrap->toggleOn(group->value() | rpl::map(_1 == kOptionCharge));
chargeWrap->finishAnimating();
}
using WeakToast = base::weak_ptr<Ui::Toast::Instance>; using WeakToast = base::weak_ptr<Ui::Toast::Instance>;
const auto toast = std::make_shared<WeakToast>(); const auto toast = std::make_shared<WeakToast>();
const auto showToast = [=] { const auto showToast = [=] {
@ -1111,20 +875,19 @@ void EditMessagesPrivacyBox(
}), }),
}); });
}; };
if (!allowed()) { if (!allowed()) {
CreateRadiobuttonLock(restricted, st::messagePrivacyCheck); CreateRadiobuttonLock(restricted, st::messagePrivacyCheck);
if (charged) {
CreateRadiobuttonLock(charged, st::messagePrivacyCheck);
}
group->setChangedCallback([=](int value) { group->setChangedCallback([=](int value) {
if (value == kOptionPremium || value == kOptionCharge) { if (value == kOptionPremium) {
group->setValue(kOptionAll); group->setValue(kOptionAll);
showToast(); showToast();
} }
}); });
}
Ui::AddDividerText(inner, tr::lng_messages_privacy_about());
if (!allowed()) {
Ui::AddSkip(inner); Ui::AddSkip(inner);
Settings::AddButtonWithIcon( Settings::AddButtonWithIcon(
inner, inner,
@ -1144,12 +907,8 @@ void EditMessagesPrivacyBox(
} else { } else {
box->addButton(tr::lng_settings_save(), [=] { box->addButton(tr::lng_settings_save(), [=] {
if (allowed()) { if (allowed()) {
const auto value = group->current(); privacy->updateNewRequirePremium(
const auto premiumRequired = (value == kOptionPremium); group->current() == kOptionPremium);
const auto chargeStars = (value == kOptionCharge)
? state->stars.current()
: 0;
privacy->updateMessagesPrivacy(premiumRequired, chargeStars);
box->closeBox(); box->closeBox();
} else { } else {
showToast(); showToast();
@ -1160,78 +919,3 @@ void EditMessagesPrivacyBox(
}); });
} }
} }
rpl::producer<int> SetupChargeSlider(
not_null<Ui::VerticalLayout*> container,
not_null<PeerData*> peer,
int savedValue) {
struct State {
rpl::variable<int> stars;
};
const auto group = !peer->isUser();
const auto state = container->lifetime().make_state<State>();
const auto chargeStars = savedValue ? savedValue : kDefaultChargeStars;
state->stars = chargeStars;
Ui::AddSubsectionTitle(container, group
? tr::lng_rights_charge_price()
: tr::lng_messages_privacy_price());
auto values = std::vector<int>();
const auto maxStars = peer->session().appConfig().paidMessageStarsMax();
if (chargeStars < kStarsMin) {
values.push_back(chargeStars);
}
for (auto i = kStarsMin; i < std::min(100, maxStars); ++i) {
values.push_back(i);
}
for (auto i = 100; i < std::min(1000, maxStars); i += 10) {
if (i < chargeStars + 10 && chargeStars < i) {
values.push_back(chargeStars);
}
values.push_back(i);
}
for (auto i = 1000; i < maxStars + 1; i += 100) {
if (i < chargeStars + 100 && chargeStars < i) {
values.push_back(chargeStars);
}
values.push_back(i);
}
const auto valuesCount = int(values.size());
const auto setStars = [=](int value) {
state->stars = value;
};
container->add(
MakeChargeStarsSlider(
container,
&st::settingsScale,
&st::settingsScaleLabel,
valuesCount,
[=](int index) { return values[index]; },
chargeStars,
maxStars,
setStars,
setStars),
st::boxRowPadding);
const auto skip = 2 * st::defaultVerticalListSkip;
Ui::AddSkip(container, skip);
auto dollars = state->stars.value() | rpl::map([=](int stars) {
const auto ratio = peer->session().appConfig().starsWithdrawRate();
const auto dollars = int(base::SafeRound(stars * ratio));
return '~' + Ui::FillAmountAndCurrency(dollars, u"USD"_q);
});
const auto percent = peer->session().appConfig().paidMessageCommission();
Ui::AddDividerText(
container,
(group
? tr::lng_rights_charge_price_about
: tr::lng_messages_privacy_price_about)(
lt_percent,
rpl::single(QString::number(percent / 10.) + '%'),
lt_amount,
std::move(dollars)));
return state->stars.value();
}

View file

@ -169,8 +169,3 @@ private:
void EditMessagesPrivacyBox( void EditMessagesPrivacyBox(
not_null<Ui::GenericBox*> box, not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> controller); not_null<Window::SessionController*> controller);
[[nodiscard]] rpl::producer<int> SetupChargeSlider(
not_null<Ui::VerticalLayout*> container,
not_null<PeerData*> peer,
int savedValue);

View file

@ -441,10 +441,13 @@ void EditFilterBox(
using namespace Window; using namespace Window;
return window->isGifPausedAtLeastFor(GifPauseReason::Layer); return window->isGifPausedAtLeastFor(GifPauseReason::Layer);
}; };
name->setCustomTextContext(Core::TextContext({ name->setCustomTextContext([=](Fn<void()> repaint) {
.session = session, return std::any(Core::MarkedTextContext{
.customEmojiLoopLimit = value ? -1 : 0, .session = session,
}), [paused] { .customEmojiRepaint = std::move(repaint),
.customEmojiLoopLimit = value ? -1 : 0,
});
}, [paused] {
return On(PowerSaving::kEmojiChat) || paused(); return On(PowerSaving::kEmojiChat) || paused();
}, [paused] { }, [paused] {
return On(PowerSaving::kChatSpoiler) || paused(); return On(PowerSaving::kChatSpoiler) || paused();
@ -606,7 +609,10 @@ void EditFilterBox(
float64 alpha = 1.; float64 alpha = 1.;
}; };
const auto tag = preview->lifetime().make_state<TagState>(); const auto tag = preview->lifetime().make_state<TagState>();
tag->context.textContext = Core::TextContext({ .session = session }); tag->context.textContext = Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [] {},
};
preview->paintRequest() | rpl::start_with_next([=] { preview->paintRequest() | rpl::start_with_next([=] {
auto p = QPainter(preview); auto p = QPainter(preview);
p.setOpacity(tag->alpha); p.setOpacity(tag->alpha);

View file

@ -163,10 +163,10 @@ ExceptionRow::ExceptionRow(
st::defaultTextStyle, st::defaultTextStyle,
filters, filters,
kMarkupTextOptions, kMarkupTextOptions,
Core::TextContext({ Core::MarkedTextContext{
.session = &history->session(), .session = &history->session(),
.repaint = repaint, .customEmojiRepaint = repaint,
})); });
} else if (peer()->isSelf()) { } else if (peer()->isSelf()) {
setCustomStatus(tr::lng_saved_forward_here(tr::now)); setCustomStatus(tr::lng_saved_forward_here(tr::now));
} }

View file

@ -537,6 +537,13 @@ void LinkController::addHeader(not_null<Ui::VerticalLayout*> container) {
verticalLayout->add(std::move(icon.widget)); verticalLayout->add(std::move(icon.widget));
const auto isStatic = _filterTitle.isStatic; const auto isStatic = _filterTitle.isStatic;
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = &_window->session(),
.customEmojiRepaint = update,
.customEmojiLoopLimit = isStatic ? -1 : 0,
};
};
verticalLayout->add( verticalLayout->add(
object_ptr<Ui::CenterWrap<>>( object_ptr<Ui::CenterWrap<>>(
verticalLayout, verticalLayout,
@ -552,10 +559,7 @@ void LinkController::addHeader(not_null<Ui::VerticalLayout*> container) {
Ui::Text::WithEntities)), Ui::Text::WithEntities)),
st::settingsFilterDividerLabel, st::settingsFilterDividerLabel,
st::defaultPopupMenu, st::defaultPopupMenu,
Core::TextContext({ makeContext)),
.session = &_window->session(),
.customEmojiLoopLimit = isStatic ? -1 : 0,
}))),
st::filterLinkDividerLabelPadding); st::filterLinkDividerLabelPadding);
verticalLayout->geometryValue( verticalLayout->geometryValue(

View file

@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_credits.h" #include "api/api_credits.h"
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "core/ui_integration.h" // TextContext.
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h" #include "data/data_user.h"
@ -68,9 +67,14 @@ void GiftCreditsBox(
2.); 2.);
{ {
Ui::AddSkip(content); Ui::AddSkip(content);
const auto arrow = Ui::Text::SingleCustomEmoji(
peer->owner().customEmojiManager().registerInternalEmoji(
st::topicButtonArrow,
st::channelEarnLearnArrowMargins,
true));
auto link = tr::lng_credits_box_history_entry_gift_about_link( auto link = tr::lng_credits_box_history_entry_gift_about_link(
lt_emoji, lt_emoji,
rpl::single(Ui::Text::IconEmoji(&st::textMoreIconEmoji)), rpl::single(arrow),
Ui::Text::RichLangValue Ui::Text::RichLangValue
) | rpl::map([](TextWithEntities text) { ) | rpl::map([](TextWithEntities text) {
return Ui::Text::Link( return Ui::Text::Link(
@ -88,7 +92,7 @@ void GiftCreditsBox(
lt_link, lt_link,
std::move(link), std::move(link),
Ui::Text::RichLangValue), Ui::Text::RichLangValue),
Core::TextContext({ .session = &peer->session() }), { .session = &peer->session() },
st::creditsBoxAbout)), st::creditsBoxAbout)),
st::boxRowPadding); st::boxRowPadding);
} }

View file

@ -19,7 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/premium_preview_box.h" // ShowPremiumPreviewBox. #include "boxes/premium_preview_box.h" // ShowPremiumPreviewBox.
#include "boxes/star_gift_box.h" // ShowStarGiftBox. #include "boxes/star_gift_box.h" // ShowStarGiftBox.
#include "boxes/transfer_gift_box.h" // ShowTransferGiftBox. #include "boxes/transfer_gift_box.h" // ShowTransferGiftBox.
#include "core/ui_integration.h"
#include "data/data_boosts.h" #include "data/data_boosts.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_channel.h" #include "data/data_channel.h"
@ -59,6 +58,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/widgets/checkbox.h" #include "ui/widgets/checkbox.h"
#include "ui/widgets/gradient_round_button.h" #include "ui/widgets/gradient_round_button.h"
#include "ui/widgets/label_with_custom_emoji.h"
#include "ui/widgets/tooltip.h" #include "ui/widgets/tooltip.h"
#include "ui/wrap/padding_wrap.h" #include "ui/wrap/padding_wrap.h"
#include "ui/wrap/slide_wrap.h" #include "ui/wrap/slide_wrap.h"
@ -66,7 +66,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_peer_menu.h" // ShowChooseRecipientBox. #include "window/window_peer_menu.h" // ShowChooseRecipientBox.
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_credits.h"
#include "styles/style_giveaway.h" #include "styles/style_giveaway.h"
#include "styles/style_info.h" #include "styles/style_info.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
@ -517,13 +516,13 @@ not_null<Ui::FlatLabel*> AddTableRow(
not_null<Ui::TableLayout*> table, not_null<Ui::TableLayout*> table,
rpl::producer<QString> label, rpl::producer<QString> label,
rpl::producer<TextWithEntities> value, rpl::producer<TextWithEntities> value,
const Ui::Text::MarkedContext &context = {}) { const Fn<std::any(Fn<void()>)> &makeContext = nullptr) {
auto widget = object_ptr<Ui::FlatLabel>( auto widget = object_ptr<Ui::FlatLabel>(
table, table,
std::move(value), std::move(value),
table->st().defaultValue, table->st().defaultValue,
st::defaultPopupMenu, st::defaultPopupMenu,
context); std::move(makeContext));
const auto result = widget.data(); const auto result = widget.data();
AddTableRow( AddTableRow(
table, table,
@ -1273,8 +1272,8 @@ void AddStarGiftTable(
const auto selfBareId = session->userPeerId().value; const auto selfBareId = session->userPeerId().value;
const auto giftToSelf = (peerId == session->userPeerId()) const auto giftToSelf = (peerId == session->userPeerId())
&& (entry.in || entry.bareGiftOwnerId == selfBareId); && (entry.in || entry.bareGiftOwnerId == selfBareId);
const auto giftToChannel = entry.giftChannelSavedId const auto giftToChannel = entry.giftSavedId
&& peerIsChannel(PeerId(entry.bareEntryOwnerId)); && peerIsChannel(PeerId(entry.bareGiftListPeerId));
const auto raw = std::make_shared<Ui::ImportantTooltip*>(nullptr); const auto raw = std::make_shared<Ui::ImportantTooltip*>(nullptr);
const auto showTooltip = [=]( const auto showTooltip = [=](
@ -1395,14 +1394,14 @@ void AddStarGiftTable(
? MakePeerTableValue(table, show, PeerId(entry.bareActorId)) ? MakePeerTableValue(table, show, PeerId(entry.bareActorId))
: MakeHiddenPeerTableValue(table)), : MakeHiddenPeerTableValue(table)),
st::giveawayGiftCodePeerMargin); st::giveawayGiftCodePeerMargin);
if (entry.bareEntryOwnerId) { if (entry.bareGiftListPeerId) {
AddTableRow( AddTableRow(
table, table,
tr::lng_credits_box_history_entry_peer(), tr::lng_credits_box_history_entry_peer(),
MakePeerTableValue( MakePeerTableValue(
table, table,
show, show,
PeerId(entry.bareEntryOwnerId)), PeerId(entry.bareGiftListPeerId)),
st::giveawayGiftCodePeerMargin); st::giveawayGiftCodePeerMargin);
} }
} else if (peerId && !giftToSelf) { } else if (peerId && !giftToSelf) {
@ -1527,6 +1526,12 @@ void AddStarGiftTable(
: nullptr; : nullptr;
const auto date = base::unixtime::parse(original.date).date(); const auto date = base::unixtime::parse(original.date).date();
const auto dateText = TextWithEntities{ langDayOfMonth(date) }; const auto dateText = TextWithEntities{ langDayOfMonth(date) };
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = std::move(update),
};
};
auto label = object_ptr<Ui::FlatLabel>( auto label = object_ptr<Ui::FlatLabel>(
table, table,
(from (from
@ -1568,7 +1573,7 @@ void AddStarGiftTable(
? *st.tableValueMessage ? *st.tableValueMessage
: st::giveawayGiftMessage), : st::giveawayGiftMessage),
st::defaultPopupMenu, st::defaultPopupMenu,
Core::TextContext({ .session = session })); makeContext);
const auto showBoxLink = [=](not_null<PeerData*> peer) { const auto showBoxLink = [=](not_null<PeerData*> peer) {
return std::make_shared<LambdaClickHandler>([=] { return std::make_shared<LambdaClickHandler>([=] {
show->showBox(PrepareShortInfoBox(peer, show)); show->showBox(PrepareShortInfoBox(peer, show));
@ -1586,6 +1591,12 @@ void AddStarGiftTable(
st::giveawayGiftCodeValueMargin); st::giveawayGiftCodeValueMargin);
} }
} else if (!entry.description.empty()) { } else if (!entry.description.empty()) {
const auto makeContext = [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = std::move(update),
};
};
auto label = object_ptr<Ui::FlatLabel>( auto label = object_ptr<Ui::FlatLabel>(
table, table,
rpl::single(entry.description), rpl::single(entry.description),
@ -1593,7 +1604,7 @@ void AddStarGiftTable(
? *st.tableValueMessage ? *st.tableValueMessage
: st::giveawayGiftMessage), : st::giveawayGiftMessage),
st::defaultPopupMenu, st::defaultPopupMenu,
Core::TextContext({ .session = session })); makeContext);
label->setSelectable(true); label->setSelectable(true);
table->addRow( table->addRow(
nullptr, nullptr,
@ -1764,25 +1775,6 @@ void AddCreditsHistoryEntryTable(
tr::lng_credits_box_history_entry_subscription( tr::lng_credits_box_history_entry_subscription(
Ui::Text::WithEntities)); Ui::Text::WithEntities));
} }
if (entry.paidMessagesAmount) {
auto value = Ui::Text::IconEmoji(&st::starIconEmojiColored);
const auto full = (entry.in ? 1 : -1)
* (entry.credits + entry.paidMessagesAmount);
const auto starsText = Lang::FormatStarsAmountDecimal(full);
AddTableRow(
table,
tr::lng_credits_paid_messages_full(),
rpl::single(value.append(' ' + starsText)));
}
if (const auto months = entry.premiumMonthsForStars) {
AddTableRow(
table,
tr::lng_credits_premium_gift_duration(),
tr::lng_months(
lt_count,
rpl::single(1. * months),
Ui::Text::WithEntities));
}
if (!entry.id.isEmpty()) { if (!entry.id.isEmpty()) {
auto label = MakeMaybeMultilineTokenValue(table, entry.id, st); auto label = MakeMaybeMultilineTokenValue(table, entry.id, st);
label->setClickHandlerFilter([=](const auto &...) { label->setClickHandlerFilter([=](const auto &...) {

View file

@ -453,7 +453,10 @@ void CreateModerateMessagesBox(
) | rpl::start_with_next([=](const TextWithEntities &text) { ) | rpl::start_with_next([=](const TextWithEntities &text) {
raw->setMarkedText( raw->setMarkedText(
Ui::Text::Link(text, u"internal:"_q), Ui::Text::Link(text, u"internal:"_q),
Core::TextContext({ .session = session })); Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [=] { raw->update(); },
});
}, label->lifetime()); }, label->lifetime());
Ui::AddSkip(inner); Ui::AddSkip(inner);

View file

@ -1154,7 +1154,8 @@ RecoverBox::RecoverBox(
rpl::single(Ui::Text::WrapEmailPattern(pattern)), rpl::single(Ui::Text::WrapEmailPattern(pattern)),
Ui::Text::WithEntities), Ui::Text::WithEntities),
st::termsContent, st::termsContent,
st::defaultPopupMenu) st::defaultPopupMenu,
[=](Fn<void()> update) { return CommonTextContext{ std::move(update) }; })
, _closeParent(std::move(closeParent)) { , _closeParent(std::move(closeParent)) {
_patternLabel->setAttribute(Qt::WA_TransparentForMouseEvents); _patternLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
if (_cloudFields.pendingResetDate != 0 || !session) { if (_cloudFields.pendingResetDate != 0 || !session) {

View file

@ -883,7 +883,6 @@ void PeerListRow::paintUserpic(
} else if (const auto callback = generatePaintUserpicCallback(false)) { } else if (const auto callback = generatePaintUserpicCallback(false)) {
callback(p, x, y, outerWidth, st.photoSize); callback(p, x, y, outerWidth, st.photoSize);
} }
paintUserpicOverlay(p, st, x, y, outerWidth);
} }
// Emulates Ui::RoundImageCheckbox::paint() in a checked state. // Emulates Ui::RoundImageCheckbox::paint() in a checked state.

View file

@ -95,13 +95,6 @@ public:
[[nodiscard]] virtual QString generateShortName(); [[nodiscard]] virtual QString generateShortName();
[[nodiscard]] virtual auto generatePaintUserpicCallback( [[nodiscard]] virtual auto generatePaintUserpicCallback(
bool forceRound) -> PaintRoundImageCallback; bool forceRound) -> PaintRoundImageCallback;
virtual void paintUserpicOverlay(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int outerWidth) {
}
[[nodiscard]] virtual auto generateNameFirstLetters() const [[nodiscard]] virtual auto generateNameFirstLetters() const
-> const base::flat_set<QChar> &; -> const base::flat_set<QChar> &;

View file

@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "api/api_chat_participants.h" #include "api/api_chat_participants.h"
#include "api/api_premium.h" // MessageMoneyRestriction. #include "api/api_premium.h"
#include "base/random.h" #include "base/random.h"
#include "boxes/filters/edit_filter_chats_list.h" #include "boxes/filters/edit_filter_chats_list.h"
#include "settings/settings_premium.h" #include "settings/settings_premium.h"
@ -41,7 +41,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "dialogs/dialogs_main_list.h" #include "dialogs/dialogs_main_list.h"
#include "payments/ui/payments_reaction_box.h"
#include "ui/effects/outline_segments.h" #include "ui/effects/outline_segments.h"
#include "ui/wrap/slide_wrap.h" #include "ui/wrap/slide_wrap.h"
#include "window/window_separate_id.h" #include "window/window_separate_id.h"
@ -276,71 +275,40 @@ bool PeerListGlobalSearchController::isLoading() {
return _timer.isActive() || _requestId; return _timer.isActive() || _requestId;
} }
struct RecipientRow::Restriction {
Api::MessageMoneyRestriction value;
RestrictionBadgeCache cache;
};
RecipientRow::RecipientRow( RecipientRow::RecipientRow(
not_null<PeerData*> peer, not_null<PeerData*> peer,
const style::PeerListItem *maybeLockedSt, const style::PeerListItem *maybeLockedSt,
History *maybeHistory) History *maybeHistory)
: PeerListRow(peer) : PeerListRow(peer)
, _maybeHistory(maybeHistory) , _maybeHistory(maybeHistory)
, _maybeLockedSt(maybeLockedSt) { , _resolvePremiumRequired(maybeLockedSt != nullptr) {
if (_maybeLockedSt) { if (maybeLockedSt
setRestriction(Api::ResolveMessageMoneyRestrictions( && (Api::ResolveRequiresPremiumToWrite(peer, maybeHistory)
peer, == Api::RequirePremiumState::Yes)) {
maybeHistory)); _lockedSt = maybeLockedSt;
} }
} }
Api::MessageMoneyRestriction RecipientRow::restriction() const { PaintRoundImageCallback RecipientRow::generatePaintUserpicCallback(
return _restriction bool forceRound) {
? _restriction->value auto result = PeerListRow::generatePaintUserpicCallback(forceRound);
: Api::MessageMoneyRestriction(); if (const auto st = _lockedSt) {
} return [=](Painter &p, int x, int y, int outerWidth, int size) {
result(p, x, y, outerWidth, size);
void RecipientRow::setRestriction(Api::MessageMoneyRestriction restriction) { PaintPremiumRequiredLock(p, st, x, y, outerWidth, size);
if (!restriction) { };
_restriction = nullptr;
return;
} else if (!_restriction) {
_restriction = std::make_unique<Restriction>();
}
_restriction->value = restriction;
}
void RecipientRow::paintUserpicOverlay(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int outerWidth) {
if (const auto &r = _restriction) {
PaintRestrictionBadge(
p,
_maybeLockedSt,
r->value.starsPerMessage,
r->cache,
x,
y,
outerWidth,
st.photoSize);
} }
return result;
} }
bool RecipientRow::refreshLock( bool RecipientRow::refreshLock(
not_null<const style::PeerListItem*> maybeLockedSt) { not_null<const style::PeerListItem*> maybeLockedSt) {
if (const auto user = peer()->asUser()) { if (const auto user = peer()->asUser()) {
using Restriction = Api::MessageMoneyRestriction; const auto locked = _resolvePremiumRequired
const auto r = _maybeLockedSt && (Api::ResolveRequiresPremiumToWrite(user, _maybeHistory)
? Api::ResolveMessageMoneyRestrictions( == Api::RequirePremiumState::Yes);
user, if (this->locked() != locked) {
_maybeHistory) setLocked(locked ? maybeLockedSt.get() : nullptr);
: Restriction();
if ((_restriction ? _restriction->value : Restriction()) != r) {
setRestriction(r);
return true; return true;
} }
} }
@ -350,30 +318,22 @@ bool RecipientRow::refreshLock(
void RecipientRow::preloadUserpic() { void RecipientRow::preloadUserpic() {
PeerListRow::preloadUserpic(); PeerListRow::preloadUserpic();
if (!_maybeLockedSt) { if (!_resolvePremiumRequired) {
return; return;
} } else if (Api::ResolveRequiresPremiumToWrite(peer(), _maybeHistory)
const auto peer = this->peer(); == Api::RequirePremiumState::Unknown) {
const auto known = Api::ResolveMessageMoneyRestrictions( const auto user = peer()->asUser();
peer, user->session().api().premium().resolvePremiumRequired(user);
_maybeHistory).known;
if (known) {
return;
} else if (const auto user = peer->asUser()) {
const auto api = &user->session().api();
api->premium().resolveMessageMoneyRestrictions(user);
} else if (const auto group = peer->asChannel()) {
group->updateFull();
} }
} }
void TrackMessageMoneyRestrictionsChanges( void TrackPremiumRequiredChanges(
not_null<PeerListController*> controller, not_null<PeerListController*> controller,
rpl::lifetime &lifetime) { rpl::lifetime &lifetime) {
const auto session = &controller->session(); const auto session = &controller->session();
rpl::merge( rpl::merge(
Data::AmPremiumValue(session) | rpl::to_empty, Data::AmPremiumValue(session) | rpl::to_empty,
session->api().premium().someMessageMoneyRestrictionsResolved() session->api().premium().somePremiumRequiredResolved()
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
const auto st = &controller->computeListSt().item; const auto st = &controller->computeListSt().item;
const auto delegate = controller->delegate(); const auto delegate = controller->delegate();
@ -766,7 +726,7 @@ std::unique_ptr<PeerListRow> ContactsBoxController::createRow(
return std::make_unique<PeerListRow>(user); return std::make_unique<PeerListRow>(user);
} }
RecipientMoneyRestrictionError WriteMoneyRestrictionError( RecipientPremiumRequiredError WritePremiumRequiredError(
not_null<UserData*> user) { not_null<UserData*> user) {
return { return {
.text = tr::lng_send_non_premium_message_toast( .text = tr::lng_send_non_premium_message_toast(
@ -799,7 +759,7 @@ ChooseRecipientBoxController::ChooseRecipientBoxController(
, _session(args.session) , _session(args.session)
, _callback(std::move(args.callback)) , _callback(std::move(args.callback))
, _filter(std::move(args.filter)) , _filter(std::move(args.filter))
, _moneyRestrictionError(std::move(args.moneyRestrictionError)) { , _premiumRequiredError(std::move(args.premiumRequiredError)) {
} }
Main::Session &ChooseRecipientBoxController::session() const { Main::Session &ChooseRecipientBoxController::session() const {
@ -809,17 +769,14 @@ Main::Session &ChooseRecipientBoxController::session() const {
void ChooseRecipientBoxController::prepareViewHook() { void ChooseRecipientBoxController::prepareViewHook() {
delegate()->peerListSetTitle(tr::lng_forward_choose()); delegate()->peerListSetTitle(tr::lng_forward_choose());
if (_moneyRestrictionError) { if (_premiumRequiredError) {
TrackMessageMoneyRestrictionsChanges(this, lifetime()); TrackPremiumRequiredChanges(this, lifetime());
} }
} }
bool ChooseRecipientBoxController::showLockedError( bool ChooseRecipientBoxController::showLockedError(
not_null<PeerListRow*> row) { not_null<PeerListRow*> row) {
return RecipientRow::ShowLockedError( return RecipientRow::ShowLockedError(this, row, _premiumRequiredError);
this,
row,
_moneyRestrictionError);
} }
void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) { void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
@ -879,9 +836,8 @@ void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
bool RecipientRow::ShowLockedError( bool RecipientRow::ShowLockedError(
not_null<PeerListController*> controller, not_null<PeerListController*> controller,
not_null<PeerListRow*> row, not_null<PeerListRow*> row,
Fn<RecipientMoneyRestrictionError(not_null<UserData*>)> error) { Fn<RecipientPremiumRequiredError(not_null<UserData*>)> error) {
const auto recipient = static_cast<RecipientRow*>(row.get()); if (!static_cast<RecipientRow*>(row.get())->locked()) {
if (!recipient->restriction().premiumRequired) {
return false; return false;
} }
::Settings::ShowPremiumPromoToast( ::Settings::ShowPremiumPromoToast(
@ -904,15 +860,15 @@ auto ChooseRecipientBoxController::createRow(
: ((peer->isBroadcast() && !Data::CanSendAnything(peer)) : ((peer->isBroadcast() && !Data::CanSendAnything(peer))
|| peer->isRepliesChat() || peer->isRepliesChat()
|| peer->isVerifyCodes() || peer->isVerifyCodes()
|| (peer->isUser() && (_moneyRestrictionError || (peer->isUser() && (_premiumRequiredError
? !peer->asUser()->canSendIgnoreMoneyRestrictions() ? !peer->asUser()->canSendIgnoreRequirePremium()
: !Data::CanSendAnything(peer)))); : !Data::CanSendAnything(peer))));
if (skip) { if (skip) {
return nullptr; return nullptr;
} }
auto result = std::make_unique<Row>( auto result = std::make_unique<Row>(
history, history,
_moneyRestrictionError ? &computeListSt().item : nullptr); _premiumRequiredError ? &computeListSt().item : nullptr);
return result; return result;
} }
@ -1137,61 +1093,25 @@ auto ChooseTopicBoxController::createRow(not_null<Data::ForumTopic*> topic)
return skip ? nullptr : std::make_unique<Row>(topic); return skip ? nullptr : std::make_unique<Row>(topic);
}; };
void PaintRestrictionBadge( void PaintPremiumRequiredLock(
Painter &p, Painter &p,
not_null<const style::PeerListItem*> st, not_null<const style::PeerListItem*> st,
int stars,
RestrictionBadgeCache &cache,
int x, int x,
int y, int y,
int outerWidth, int outerWidth,
int size) { int size) {
const auto paletteVersion = style::PaletteVersion(); auto hq = PainterHighQualityEnabler(p);
const auto good = !cache.badge.isNull()
&& (cache.stars == stars)
&& (cache.paletteVersion == paletteVersion);
const auto &check = st->checkbox.check; const auto &check = st->checkbox.check;
const auto add = check.width; auto pen = check.border->p;
if (!good) { pen.setWidthF(check.width);
cache.stars = stars; p.setPen(pen);
cache.paletteVersion = paletteVersion; p.setBrush(st::premiumButtonBg2);
if (stars) { const auto &icon = st::stickersPremiumLock;
const auto text = (stars >= 1000) const auto width = icon.width();
? (QString::number(stars / 1000) + 'K') const auto height = icon.height();
: QString::number(stars); const auto rect = QRect(
cache.badge = Ui::GenerateSmallBadgeImage( QPoint(x + size - width, y + size - height),
text, icon.size());
st::paidReactTopStarIcon, p.drawEllipse(rect);
check.bgActive->c, icon.paintInCenter(p, rect);
st::premiumButtonFg->c,
&check);
} else {
auto hq = PainterHighQualityEnabler(p);
const auto &icon = st::stickersPremiumLock;
const auto width = icon.width();
const auto height = icon.height();
const auto rect = QRect(
QPoint(x + size - width, y + size - height),
icon.size());
const auto added = QMargins(add, add, add, add);
const auto ratio = style::DevicePixelRatio();
cache.badge = QImage(
(rect + added).size() * ratio,
QImage::Format_ARGB32_Premultiplied);
cache.badge.setDevicePixelRatio(ratio);
cache.badge.fill(Qt::transparent);
const auto inner = QRect(add, add, rect.width(), rect.height());
auto q = QPainter(&cache.badge);
auto pen = check.border->p;
pen.setWidthF(check.width);
q.setPen(pen);
q.setBrush(st::premiumButtonBg2);
q.drawEllipse(inner);
icon.paintInCenter(q, inner);
}
}
const auto cached = cache.badge.size() / cache.badge.devicePixelRatio();
const auto left = x + size + add - cached.width();
const auto top = stars ? (y - add) : (y + size + add - cached.height());
p.drawImage(left, top, cache.badge);
} }

View file

@ -19,10 +19,6 @@ namespace style {
struct PeerListItem; struct PeerListItem;
} // namespace style } // namespace style
namespace Api {
struct MessageMoneyRestriction;
} // namespace Api
namespace Data { namespace Data {
class Thread; class Thread;
class Forum; class Forum;
@ -97,28 +93,13 @@ private:
}; };
struct RecipientMoneyRestrictionError { struct RecipientPremiumRequiredError {
TextWithEntities text; TextWithEntities text;
}; };
[[nodiscard]] RecipientMoneyRestrictionError WriteMoneyRestrictionError( [[nodiscard]] RecipientPremiumRequiredError WritePremiumRequiredError(
not_null<UserData*> user); not_null<UserData*> user);
struct RestrictionBadgeCache {
int paletteVersion = 0;
int stars = 0;
QImage badge;
};
void PaintRestrictionBadge(
Painter &p,
not_null<const style::PeerListItem*> st,
int stars,
RestrictionBadgeCache &cache,
int x,
int y,
int outerWidth,
int size);
class RecipientRow : public PeerListRow { class RecipientRow : public PeerListRow {
public: public:
explicit RecipientRow( explicit RecipientRow(
@ -131,33 +112,30 @@ public:
[[nodiscard]] static bool ShowLockedError( [[nodiscard]] static bool ShowLockedError(
not_null<PeerListController*> controller, not_null<PeerListController*> controller,
not_null<PeerListRow*> row, not_null<PeerListRow*> row,
Fn<RecipientMoneyRestrictionError(not_null<UserData*>)> error); Fn<RecipientPremiumRequiredError(not_null<UserData*>)> error);
[[nodiscard]] History *maybeHistory() const { [[nodiscard]] History *maybeHistory() const {
return _maybeHistory; return _maybeHistory;
} }
void paintUserpicOverlay( [[nodiscard]] bool locked() const {
Painter &p, return _lockedSt != nullptr;
const style::PeerListItem &st, }
int x, void setLocked(const style::PeerListItem *lockedSt) {
int y, _lockedSt = lockedSt;
int outerWidth) override; }
PaintRoundImageCallback generatePaintUserpicCallback(
bool forceRound) override;
void preloadUserpic() override; void preloadUserpic() override;
[[nodiscard]] Api::MessageMoneyRestriction restriction() const;
void setRestriction(Api::MessageMoneyRestriction restriction);
private: private:
struct Restriction;
History *_maybeHistory = nullptr; History *_maybeHistory = nullptr;
const style::PeerListItem *_maybeLockedSt = nullptr; const style::PeerListItem *_lockedSt = nullptr;
std::shared_ptr<Restriction> _restriction; bool _resolvePremiumRequired = false;
}; };
void TrackMessageMoneyRestrictionsChanges( void TrackPremiumRequiredChanges(
not_null<PeerListController*> controller, not_null<PeerListController*> controller,
rpl::lifetime &lifetime); rpl::lifetime &lifetime);
@ -283,8 +261,8 @@ struct ChooseRecipientArgs {
FnMut<void(not_null<Data::Thread*>)> callback; FnMut<void(not_null<Data::Thread*>)> callback;
Fn<bool(not_null<Data::Thread*>)> filter; Fn<bool(not_null<Data::Thread*>)> filter;
using MoneyRestrictionError = RecipientMoneyRestrictionError; using PremiumRequiredError = RecipientPremiumRequiredError;
Fn<MoneyRestrictionError(not_null<UserData*>)> moneyRestrictionError; Fn<PremiumRequiredError(not_null<UserData*>)> premiumRequiredError;
}; };
class ChooseRecipientBoxController class ChooseRecipientBoxController
@ -312,8 +290,8 @@ private:
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
FnMut<void(not_null<Data::Thread*>)> _callback; FnMut<void(not_null<Data::Thread*>)> _callback;
Fn<bool(not_null<Data::Thread*>)> _filter; Fn<bool(not_null<Data::Thread*>)> _filter;
Fn<RecipientMoneyRestrictionError( Fn<RecipientPremiumRequiredError(
not_null<UserData*>)> _moneyRestrictionError; not_null<UserData*>)> _premiumRequiredError;
}; };
@ -393,3 +371,11 @@ private:
Fn<bool(not_null<Data::ForumTopic*>)> _filter; Fn<bool(not_null<Data::ForumTopic*>)> _filter;
}; };
void PaintPremiumRequiredLock(
Painter &p,
not_null<const style::PeerListItem*> st,
int x,
int y,
int outerWidth,
int size);

View file

@ -9,12 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_chat_participants.h" #include "api/api_chat_participants.h"
#include "api/api_invite_links.h" #include "api/api_invite_links.h"
#include "api/api_premium.h"
#include "boxes/peers/edit_participant_box.h" #include "boxes/peers/edit_participant_box.h"
#include "boxes/peers/edit_peer_type_box.h" #include "boxes/peers/edit_peer_type_box.h"
#include "boxes/peers/replace_boost_box.h" #include "boxes/peers/replace_boost_box.h"
#include "boxes/max_invite_box.h" #include "boxes/max_invite_box.h"
#include "chat_helpers/message_field.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_chat.h" #include "data/data_chat.h"
@ -24,7 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item_helpers.h"
#include "dialogs/dialogs_indexed_list.h" #include "dialogs/dialogs_indexed_list.h"
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "ui/boxes/show_or_premium_box.h" #include "ui/boxes/show_or_premium_box.h"
@ -55,39 +52,16 @@ constexpr auto kUserpicsLimit = 3;
class ForbiddenRow final : public PeerListRow { class ForbiddenRow final : public PeerListRow {
public: public:
ForbiddenRow( ForbiddenRow(not_null<PeerData*> peer, bool locked);
not_null<PeerData*> peer,
not_null<const style::PeerListItem*> lockSt,
bool locked);
PaintRoundImageCallback generatePaintUserpicCallback( PaintRoundImageCallback generatePaintUserpicCallback(
bool forceRound) override; bool forceRound) override;
Api::MessageMoneyRestriction restriction() const;
void setRestriction(Api::MessageMoneyRestriction restriction);
void preloadUserpic() override;
void paintUserpicOverlay(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int outerWidth) override;
bool refreshLock();
private: private:
struct Restriction {
Api::MessageMoneyRestriction value;
RestrictionBadgeCache cache;
};
const bool _locked = false; const bool _locked = false;
const not_null<const style::PeerListItem*> _lockSt;
QImage _disabledFrame; QImage _disabledFrame;
InMemoryKey _userpicKey; InMemoryKey _userpicKey;
int _paletteVersion = 0; int _paletteVersion = 0;
std::shared_ptr<Restriction> _restriction;
}; };
@ -107,9 +81,6 @@ public:
[[nodiscard]] rpl::producer<int> selectedValue() const { [[nodiscard]] rpl::producer<int> selectedValue() const {
return _selected.value(); return _selected.value();
} }
[[nodiscard]] rpl::producer<int> starsToSend() const {
return _starsToSend.value();
}
void send( void send(
std::vector<not_null<PeerData*>> list, std::vector<not_null<PeerData*>> list,
@ -118,16 +89,10 @@ public:
private: private:
void appendRow(not_null<UserData*> user); void appendRow(not_null<UserData*> user);
[[nodiscard]] std::unique_ptr<ForbiddenRow> createRow( [[nodiscard]] std::unique_ptr<PeerListRow> createRow(
not_null<UserData*> user) const; not_null<UserData*> user) const;
[[nodiscard]] bool canInvite(not_null<PeerData*> peer) const; [[nodiscard]] bool canInvite(not_null<PeerData*> peer) const;
void send(
std::vector<not_null<PeerData*>> list,
Ui::ShowPtr show,
Fn<void()> close,
Api::SendOptions options);
void setSimpleCover(); void setSimpleCover();
void setComplexCover(); void setComplexCover();
@ -136,11 +101,8 @@ private:
const std::vector<not_null<UserData*>> &_users; const std::vector<not_null<UserData*>> &_users;
const bool _can = false; const bool _can = false;
rpl::variable<int> _selected; rpl::variable<int> _selected;
rpl::variable<int> _starsToSend;
bool _sending = false; bool _sending = false;
rpl::lifetime _paymentCheckLifetime;
}; };
base::flat_set<not_null<UserData*>> GetAlreadyInFromPeer(PeerData *peer) { base::flat_set<not_null<UserData*>> GetAlreadyInFromPeer(PeerData *peer) {
@ -294,17 +256,11 @@ Main::Session &InviteForbiddenController::session() const {
return _peer->session(); return _peer->session();
} }
ForbiddenRow::ForbiddenRow( ForbiddenRow::ForbiddenRow(not_null<PeerData*> peer, bool locked)
not_null<PeerData*> peer,
not_null<const style::PeerListItem*> lockSt,
bool locked)
: PeerListRow(peer) : PeerListRow(peer)
, _locked(locked) , _locked(locked) {
, _lockSt(lockSt) {
if (_locked) { if (_locked) {
setCustomStatus(tr::lng_invite_status_disabled(tr::now)); setCustomStatus(tr::lng_invite_status_disabled(tr::now));
} else {
setRestriction(Api::ResolveMessageMoneyRestrictions(peer, nullptr));
} }
} }
@ -383,76 +339,6 @@ PaintRoundImageCallback ForbiddenRow::generatePaintUserpicCallback(
}; };
} }
Api::MessageMoneyRestriction ForbiddenRow::restriction() const {
return _restriction
? _restriction->value
: Api::MessageMoneyRestriction();
}
void ForbiddenRow::setRestriction(Api::MessageMoneyRestriction restriction) {
if (!restriction || !restriction.starsPerMessage) {
_restriction = nullptr;
return;
} else if (!_restriction) {
_restriction = std::make_unique<Restriction>();
}
_restriction->value = restriction;
}
void ForbiddenRow::paintUserpicOverlay(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int outerWidth) {
if (const auto &r = _restriction) {
PaintRestrictionBadge(
p,
_lockSt,
r->value.starsPerMessage,
r->cache,
x,
y,
outerWidth,
st.photoSize);
}
}
bool ForbiddenRow::refreshLock() {
if (_locked) {
return false;
} else if (const auto user = peer()->asUser()) {
using Restriction = Api::MessageMoneyRestriction;
auto r = Api::ResolveMessageMoneyRestrictions(user, nullptr);
if (!r || !r.starsPerMessage) {
r = Restriction();
}
if ((_restriction ? _restriction->value : Restriction()) != r) {
setRestriction(r);
return true;
}
}
return false;
}
void ForbiddenRow::preloadUserpic() {
PeerListRow::preloadUserpic();
const auto peer = this->peer();
const auto known = Api::ResolveMessageMoneyRestrictions(
peer,
nullptr).known;
if (known) {
return;
} else if (const auto user = peer->asUser()) {
const auto api = &user->session().api();
api->premium().resolveMessageMoneyRestrictions(user);
} else if (const auto group = peer->asChannel()) {
group->updateFull();
}
}
void InviteForbiddenController::setSimpleCover() { void InviteForbiddenController::setSimpleCover() {
delegate()->peerListSetTitle( delegate()->peerListSetTitle(
_can ? tr::lng_profile_add_via_link() : tr::lng_via_link_cant()); _can ? tr::lng_profile_add_via_link() : tr::lng_via_link_cant());
@ -549,30 +435,6 @@ void InviteForbiddenController::setComplexCover() {
} }
void InviteForbiddenController::prepare() { void InviteForbiddenController::prepare() {
session().api().premium().someMessageMoneyRestrictionsResolved(
) | rpl::start_with_next([=] {
auto stars = 0;
const auto process = [&](not_null<PeerListRow*> raw) {
const auto row = static_cast<ForbiddenRow*>(raw.get());
if (row->refreshLock()) {
delegate()->peerListUpdateRow(raw);
}
if (const auto r = row->restriction()) {
stars += r.starsPerMessage;
}
};
auto count = delegate()->peerListFullRowsCount();
for (auto i = 0; i != count; ++i) {
process(delegate()->peerListRowAt(i));
}
_starsToSend = stars;
count = delegate()->peerListSearchRowsCount();
for (auto i = 0; i != count; ++i) {
process(delegate()->peerListSearchRowAt(i));
}
}, lifetime());
if (session().premium() if (session().premium()
|| (_forbidden.premiumAllowsInvite.empty() || (_forbidden.premiumAllowsInvite.empty()
&& _forbidden.premiumAllowsWrite.empty())) { && _forbidden.premiumAllowsWrite.empty())) {
@ -602,11 +464,6 @@ void InviteForbiddenController::rowClicked(not_null<PeerListRow*> row) {
const auto checked = row->checked(); const auto checked = row->checked();
delegate()->peerListSetRowChecked(row, !checked); delegate()->peerListSetRowChecked(row, !checked);
_selected = _selected.current() + (checked ? -1 : 1); _selected = _selected.current() + (checked ? -1 : 1);
const auto r = static_cast<ForbiddenRow*>(row.get())->restriction();
if (r.starsPerMessage) {
_starsToSend = _starsToSend.current()
+ (checked ? -r.starsPerMessage : r.starsPerMessage);
}
} }
void InviteForbiddenController::appendRow(not_null<UserData*> user) { void InviteForbiddenController::appendRow(not_null<UserData*> user) {
@ -616,9 +473,6 @@ void InviteForbiddenController::appendRow(not_null<UserData*> user) {
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
if (canInvite(user)) { if (canInvite(user)) {
delegate()->peerListSetRowChecked(raw, true); delegate()->peerListSetRowChecked(raw, true);
if (const auto r = raw->restriction()) {
_starsToSend = _starsToSend.current() + r.starsPerMessage;
}
} }
} }
} }
@ -627,64 +481,7 @@ void InviteForbiddenController::send(
std::vector<not_null<PeerData*>> list, std::vector<not_null<PeerData*>> list,
Ui::ShowPtr show, Ui::ShowPtr show,
Fn<void()> close) { Fn<void()> close) {
send(list, show, close, {}); if (_sending || list.empty()) {
}
void InviteForbiddenController::send(
std::vector<not_null<PeerData*>> list,
Ui::ShowPtr show,
Fn<void()> close,
Api::SendOptions options) {
if (list.empty()) {
return;
}
_paymentCheckLifetime.destroy();
const auto withPaymentApproved = [=](int approved) {
auto copy = options;
copy.starsApproved = approved;
send(list, show, close, copy);
};
const auto messagesCount = 1;
const auto alreadyApproved = options.starsApproved;
auto paid = std::vector<not_null<PeerData*>>();
auto waiting = base::flat_set<not_null<PeerData*>>();
auto totalStars = 0;
for (const auto &peer : list) {
const auto details = ComputePaymentDetails(peer, messagesCount);
if (!details) {
waiting.emplace(peer);
} else if (details->stars > 0) {
totalStars += details->stars;
paid.push_back(peer);
}
}
if (!waiting.empty()) {
session().changes().peerUpdates(
Data::PeerUpdate::Flag::FullInfo
) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
if (waiting.contains(update.peer)) {
withPaymentApproved(alreadyApproved);
}
}, _paymentCheckLifetime);
if (!session().credits().loaded()) {
session().credits().loadedValue(
) | rpl::filter(
rpl::mappers::_1
) | rpl::take(1) | rpl::start_with_next([=] {
withPaymentApproved(alreadyApproved);
}, _paymentCheckLifetime);
}
return;
} else if (totalStars > alreadyApproved) {
const auto sessionShow = Main::MakeSessionShow(show, &session());
ShowSendPaidConfirm(sessionShow, paid, SendPaymentDetails{
.messages = messagesCount,
.stars = totalStars,
}, [=] { withPaymentApproved(totalStars); });
return;
} else if (_sending) {
return; return;
} }
_sending = true; _sending = true;
@ -695,18 +492,12 @@ void InviteForbiddenController::send(
if (link.isEmpty()) { if (link.isEmpty()) {
return false; return false;
} }
auto full = options;
auto &api = _peer->session().api(); auto &api = _peer->session().api();
auto options = Api::SendOptions();
for (const auto &to : list) { for (const auto &to : list) {
auto copy = full;
copy.starsApproved = std::min(
to->starsPerMessageChecked(),
full.starsApproved);
full.starsApproved -= copy.starsApproved;
const auto history = to->owner().history(to); const auto history = to->owner().history(to);
auto message = Api::MessageToSend( auto message = Api::MessageToSend(
Api::SendAction(history, copy)); Api::SendAction(history, options));
message.textWithTags = { link }; message.textWithTags = { link };
message.action.clearDraft = false; message.action.clearDraft = false;
api.sendMessage(std::move(message)); api.sendMessage(std::move(message));
@ -751,11 +542,10 @@ void InviteForbiddenController::send(
} }
} }
std::unique_ptr<ForbiddenRow> InviteForbiddenController::createRow( std::unique_ptr<PeerListRow> InviteForbiddenController::createRow(
not_null<UserData*> user) const { not_null<UserData*> user) const {
const auto locked = _can && !canInvite(user); const auto locked = _can && !canInvite(user);
const auto lockSt = &computeListSt().item; return std::make_unique<ForbiddenRow>(user, locked);
return std::make_unique<ForbiddenRow>(user, lockSt, locked);
} }
} // namespace } // namespace
@ -794,8 +584,8 @@ void AddParticipantsBoxController::subscribeToMigration() {
} }
void AddParticipantsBoxController::rowClicked(not_null<PeerListRow*> row) { void AddParticipantsBoxController::rowClicked(not_null<PeerListRow*> row) {
const auto moneyRestrictionError = WriteMoneyRestrictionError; const auto premiumRequiredError = WritePremiumRequiredError;
if (RecipientRow::ShowLockedError(this, row, moneyRestrictionError)) { if (RecipientRow::ShowLockedError(this, row, premiumRequiredError)) {
return; return;
} }
const auto &serverConfig = session().serverConfig(); const auto &serverConfig = session().serverConfig();
@ -824,7 +614,7 @@ void AddParticipantsBoxController::itemDeselectedHook(
void AddParticipantsBoxController::prepareViewHook() { void AddParticipantsBoxController::prepareViewHook() {
updateTitle(); updateTitle();
TrackMessageMoneyRestrictionsChanges(this, lifetime()); TrackPremiumRequiredChanges(this, lifetime());
} }
int AddParticipantsBoxController::alreadyInCount() const { int AddParticipantsBoxController::alreadyInCount() const {
@ -1139,15 +929,12 @@ bool ChatInviteForbidden(
) | rpl::start_with_next([=](bool has) { ) | rpl::start_with_next([=](bool has) {
box->clearButtons(); box->clearButtons();
if (has) { if (has) {
const auto send = box->addButton(tr::lng_via_link_send(), [=] { box->addButton(tr::lng_via_link_send(), [=] {
weak->send( weak->send(
box->collectSelectedRows(), box->collectSelectedRows(),
box->uiShow(), box->uiShow(),
crl::guard(box, [=] { box->closeBox(); })); crl::guard(box, [=] { box->closeBox(); }));
}); });
send->setText(PaidSendButtonText(
weak->starsToSend(),
tr::lng_via_link_send()));
} }
box->addButton(tr::lng_create_group_skip(), [=] { box->addButton(tr::lng_create_group_skip(), [=] {
box->closeBox(); box->closeBox();

View file

@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/background_box.h" #include "boxes/background_box.h"
#include "boxes/stickers_box.h" #include "boxes/stickers_box.h"
#include "chat_helpers/compose/compose_show.h" #include "chat_helpers/compose/compose_show.h"
#include "core/ui_integration.h" // TextContext #include "core/ui_integration.h" // Core::MarkedTextContext.
#include "data/stickers/data_custom_emoji.h" #include "data/stickers/data_custom_emoji.h"
#include "data/stickers/data_stickers.h" #include "data/stickers/data_stickers.h"
#include "data/data_changes.h" #include "data/data_changes.h"
@ -165,7 +165,7 @@ private:
const uint32 _level; const uint32 _level;
const TextWithEntities _icon; const TextWithEntities _icon;
const Ui::Text::MarkedContext _context; const Core::MarkedTextContext _context;
Ui::Text::String _text; Ui::Text::String _text;
bool _minimal = false; bool _minimal = false;
@ -466,10 +466,7 @@ LevelBadge::LevelBadge(
st::settingsLevelBadgeLock, st::settingsLevelBadgeLock,
QMargins(0, st::settingsLevelBadgeLockSkip, 0, 0), QMargins(0, st::settingsLevelBadgeLockSkip, 0, 0),
false))) false)))
, _context(Core::TextContext({ , _context({ .session = session }) {
.session = session,
.repaint = [this] { update(); },
})) {
updateText(); updateText();
} }

View file

@ -219,33 +219,6 @@ void SaveSlowmodeSeconds(
api->registerModifyRequest(key, requestId); api->registerModifyRequest(key, requestId);
} }
void SaveStarsPerMessage(
not_null<ChannelData*> channel,
int starsPerMessage,
Fn<void()> done) {
const auto api = &channel->session().api();
const auto key = Api::RequestKey("stars_per_message", channel->id);
const auto requestId = api->request(MTPchannels_UpdatePaidMessagesPrice(
channel->inputChannel,
MTP_long(starsPerMessage)
)).done([=](const MTPUpdates &result) {
api->clearModifyRequest(key);
api->applyUpdates(result);
channel->setStarsPerMessage(starsPerMessage);
done();
}).fail([=](const MTP::Error &error) {
api->clearModifyRequest(key);
if (error.type() != u"CHAT_NOT_MODIFIED"_q) {
return;
}
channel->setStarsPerMessage(starsPerMessage);
done();
}).send();
api->registerModifyRequest(key, requestId);
}
void SaveBoostsUnrestrict( void SaveBoostsUnrestrict(
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
int boostsUnrestrict, int boostsUnrestrict,
@ -298,7 +271,6 @@ void ShowEditPermissions(
channel, channel,
result.boostsUnrestrict, result.boostsUnrestrict,
close); close);
SaveStarsPerMessage(channel, result.starsPerMessage, close);
} }
}; };
auto done = [=](EditPeerPermissionsBoxResult result) { auto done = [=](EditPeerPermissionsBoxResult result) {
@ -310,9 +282,7 @@ void ShowEditPermissions(
const auto saveFor = peer->migrateToOrMe(); const auto saveFor = peer->migrateToOrMe();
const auto chat = saveFor->asChat(); const auto chat = saveFor->asChat();
if (!chat if (!chat
|| (!result.slowmodeSeconds || (!result.slowmodeSeconds && !result.boostsUnrestrict)) {
&& !result.boostsUnrestrict
&& !result.starsPerMessage)) {
save(saveFor, result); save(saveFor, result);
return; return;
} }
@ -2719,9 +2689,3 @@ bool EditPeerInfoBox::Available(not_null<PeerData*> peer) {
return false; return false;
} }
} }
void ShowEditChatPermissions(
not_null<Window::SessionNavigation*> navigation,
not_null<PeerData*> peer) {
ShowEditPermissions(navigation, peer);
}

View file

@ -56,7 +56,3 @@ private:
not_null<PeerData*> _peer; not_null<PeerData*> _peer;
}; };
void ShowEditChatPermissions(
not_null<Window::SessionNavigation*> navigation,
not_null<PeerData*> peer);

View file

@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "boxes/share_box.h" #include "boxes/share_box.h"
#include "core/application.h" #include "core/application.h"
#include "core/ui_integration.h" // TextContext #include "core/ui_integration.h" // Core::MarkedTextContext.
#include "data/components/credits.h" #include "data/components/credits.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_channel.h" #include "data/data_channel.h"
@ -740,10 +740,10 @@ void Controller::setupAboveJoinedWidget() {
{ QString::number(current.subscription.credits) }, { QString::number(current.subscription.credits) },
Ui::Text::WithEntities), Ui::Text::WithEntities),
kMarkupTextOptions, kMarkupTextOptions,
Core::TextContext({ Core::MarkedTextContext{
.session = &session(), .session = &session(),
.repaint = [=] { widget->update(); }, .customEmojiRepaint = [=] { widget->update(); },
})); });
auto &lifetime = widget->lifetime(); auto &lifetime = widget->lifetime();
const auto rateValue = lifetime.make_state<rpl::variable<float64>>( const auto rateValue = lifetime.make_state<rpl::variable<float64>>(
session().credits().rateValue(_peer)); session().credits().rateValue(_peer));
@ -994,7 +994,10 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
lt_cost, lt_cost,
{ QString::number(data.subscription.credits) }, { QString::number(data.subscription.credits) },
Ui::Text::WithEntities), Ui::Text::WithEntities),
Core::TextContext({ .session = session })); Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [=] { subtitle1->update(); },
});
const auto subtitle2 = box->addRow( const auto subtitle2 = box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>( object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
box, box,
@ -1481,12 +1484,8 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
? tr::lng_group_invite_copied(tr::now) ? tr::lng_group_invite_copied(tr::now)
: copied); : copied);
}; };
auto countMessagesCallback = [=](const TextWithTags &comment) {
return 1;
};
auto submitCallback = [=]( auto submitCallback = [=](
std::vector<not_null<Data::Thread*>> &&result, std::vector<not_null<Data::Thread*>> &&result,
Fn<bool()> checkPaid,
TextWithTags &&comment, TextWithTags &&comment,
Api::SendOptions options, Api::SendOptions options,
Data::ForwardOptions) { Data::ForwardOptions) {
@ -1504,8 +1503,6 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
result.size() > 1)); result.size() > 1));
} }
return; return;
} else if (!checkPaid()) {
return;
} }
*sending = true; *sending = true;
@ -1533,7 +1530,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
}; };
auto filterCallback = [](not_null<Data::Thread*> thread) { auto filterCallback = [](not_null<Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) { if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreMoneyRestrictions()) { if (user->canSendIgnoreRequirePremium()) {
return true; return true;
} }
} }
@ -1542,10 +1539,9 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
auto object = Box<ShareBox>(ShareBox::Descriptor{ auto object = Box<ShareBox>(ShareBox::Descriptor{
.session = session, .session = session,
.copyCallback = std::move(copyCallback), .copyCallback = std::move(copyCallback),
.countMessagesCallback = std::move(countMessagesCallback),
.submitCallback = std::move(submitCallback), .submitCallback = std::move(submitCallback),
.filterCallback = std::move(filterCallback), .filterCallback = std::move(filterCallback),
.moneyRestrictionError = ShareMessageMoneyRestrictionError(), .premiumRequiredError = SharePremiumRequiredError(),
}); });
*box = Ui::MakeWeak(object.data()); *box = Ui::MakeWeak(object.data());
return object; return object;

View file

@ -31,7 +31,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/profile/info_profile_values.h" #include "info/profile/info_profile_values.h"
#include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_participants_box.h"
#include "boxes/peers/edit_peer_info_box.h" #include "boxes/peers/edit_peer_info_box.h"
#include "boxes/edit_privacy_box.h"
#include "settings/settings_power_saving.h" #include "settings/settings_power_saving.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "window/window_controller.h" #include "window/window_controller.h"
@ -892,10 +891,11 @@ void AddBoostsUnrestrictLabels(
manager->registerInternalEmoji( manager->registerInternalEmoji(
st::boostsMessageIcon, st::boostsMessageIcon,
st::boostsMessageIconPadding)); st::boostsMessageIconPadding));
const auto context = Core::TextContext({ const auto context = Core::MarkedTextContext{
.session = session, .session = session,
.customEmojiRepaint = [] {},
.customEmojiLoopLimit = 1, .customEmojiLoopLimit = 1,
}); };
for (auto i = 0; i != kBoostsUnrestrictValues; ++i) { for (auto i = 0; i != kBoostsUnrestrictValues; ++i) {
const auto label = Ui::CreateChild<Ui::FlatLabel>( const auto label = Ui::CreateChild<Ui::FlatLabel>(
labels, labels,
@ -942,7 +942,9 @@ rpl::producer<int> AddBoostsUnrestrictSlider(
const auto boostsUnrestrict = lifetime.make_state<rpl::variable<int>>( const auto boostsUnrestrict = lifetime.make_state<rpl::variable<int>>(
channel ? channel->boostsUnrestrict() : 0); channel ? channel->boostsUnrestrict() : 0);
Ui::AddSkip(container); container->add(
object_ptr<Ui::BoxContentDivider>(container),
{ 0, st::infoProfileSkip, 0, st::infoProfileSkip });
auto enabled = boostsUnrestrict->value( auto enabled = boostsUnrestrict->value(
) | rpl::map(_1 > 0); ) | rpl::map(_1 > 0);
@ -1006,20 +1008,19 @@ rpl::producer<int> AddBoostsUnrestrictWrapped(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container, container,
object_ptr<Ui::VerticalLayout>(container))); object_ptr<Ui::VerticalLayout>(container)));
wrap->toggleOn(std::move(shown), anim::type::normal); wrap->toggleOn(rpl::duplicate(shown), anim::type::normal);
wrap->finishAnimating(); wrap->finishAnimating();
const auto inner = wrap->entity(); auto result = AddBoostsUnrestrictSlider(wrap->entity(), peer);
const auto divider = container->add(
auto result = AddBoostsUnrestrictSlider(inner, peer);
const auto skip = st::defaultVerticalListSkip;
const auto divider = inner->add(
object_ptr<Ui::SlideWrap<Ui::BoxContentDivider>>( object_ptr<Ui::SlideWrap<Ui::BoxContentDivider>>(
inner, container,
object_ptr<Ui::BoxContentDivider>(inner), object_ptr<Ui::BoxContentDivider>(container),
QMargins{ 0, skip, 0, skip })); QMargins{ 0, st::infoProfileSkip, 0, st::infoProfileSkip }));
divider->toggleOn(rpl::duplicate(result) | rpl::map(!rpl::mappers::_1)); divider->toggleOn(rpl::combine(
std::move(shown),
rpl::duplicate(result),
!rpl::mappers::_1 || !rpl::mappers::_2));
divider->finishAnimating(); divider->finishAnimating();
return result; return result;
@ -1158,43 +1159,7 @@ void ShowEditPeerPermissionsBox(
rpl::variable<int> slowmodeSeconds; rpl::variable<int> slowmodeSeconds;
rpl::variable<int> boostsUnrestrict; rpl::variable<int> boostsUnrestrict;
rpl::variable<bool> hasSendRestrictions; rpl::variable<bool> hasSendRestrictions;
rpl::variable<int> starsPerMessage;
}; };
const auto state = inner->lifetime().make_state<State>();
const auto channel = peer->asChannel();
const auto available = channel && channel->paidMessagesAvailable();
Ui::AddSkip(inner);
Ui::AddDivider(inner);
auto charging = (Ui::SettingsButton*)nullptr;
if (available) {
Ui::AddSkip(inner);
const auto starsPerMessage = peer->isChannel()
? peer->asChannel()->starsPerMessage()
: 0;
charging = inner->add(object_ptr<Ui::SettingsButton>(
inner,
tr::lng_rights_charge_stars(),
st::settingsButtonNoIcon));
charging->toggleOn(rpl::single(starsPerMessage > 0));
Ui::AddSkip(inner);
Ui::AddDividerText(inner, tr::lng_rights_charge_stars_about());
const auto chargeWrap = inner->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
inner,
object_ptr<Ui::VerticalLayout>(inner)));
chargeWrap->toggleOn(charging->toggledValue());
chargeWrap->finishAnimating();
const auto chargeInner = chargeWrap->entity();
Ui::AddSkip(chargeInner);
state->starsPerMessage = SetupChargeSlider(
chargeInner,
peer,
starsPerMessage);
}
static constexpr auto kSendRestrictions = Flag::EmbedLinks static constexpr auto kSendRestrictions = Flag::EmbedLinks
| Flag::SendGames | Flag::SendGames
| Flag::SendGifs | Flag::SendGifs
@ -1208,6 +1173,7 @@ void ShowEditPeerPermissionsBox(
| Flag::SendVoiceMessages | Flag::SendVoiceMessages
| Flag::SendFiles | Flag::SendFiles
| Flag::SendOther; | Flag::SendOther;
const auto state = inner->lifetime().make_state<State>();
state->hasSendRestrictions = ((restrictions & kSendRestrictions) != 0) state->hasSendRestrictions = ((restrictions & kSendRestrictions) != 0)
|| (peer->isChannel() && peer->asChannel()->slowmodeSeconds() > 0); || (peer->isChannel() && peer->asChannel()->slowmodeSeconds() > 0);
state->boostsUnrestrict = AddBoostsUnrestrictWrapped( state->boostsUnrestrict = AddBoostsUnrestrictWrapped(
@ -1248,14 +1214,10 @@ void ShowEditPeerPermissionsBox(
const auto boostsUnrestrict = hasRestrictions const auto boostsUnrestrict = hasRestrictions
? state->boostsUnrestrict.current() ? state->boostsUnrestrict.current()
: 0; : 0;
const auto starsPerMessage = (charging && charging->toggled())
? state->starsPerMessage.current()
: 0;
done({ done({
restrictions, restrictions,
slowmodeSeconds, slowmodeSeconds,
boostsUnrestrict, boostsUnrestrict,
starsPerMessage,
}); });
}); });
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });

View file

@ -39,7 +39,6 @@ struct EditPeerPermissionsBoxResult final {
ChatRestrictions rights; ChatRestrictions rights;
int slowmodeSeconds = 0; int slowmodeSeconds = 0;
int boostsUnrestrict = 0; int boostsUnrestrict = 0;
int starsPerMessage = 0;
}; };
void ShowEditPeerPermissionsBox( void ShowEditPeerPermissionsBox(

View file

@ -363,17 +363,12 @@ object_ptr<Ui::RpWidget> AddReactionsSelector(
const auto customEmojiPaused = [controller = args.controller] { const auto customEmojiPaused = [controller = args.controller] {
return controller->isGifPausedAtLeastFor(PauseReason::Layer); return controller->isGifPausedAtLeastFor(PauseReason::Layer);
}; };
auto simpleContext = Core::TextContext({ auto factory = [=](QStringView data, Fn<void()> update)
.session = session, -> std::unique_ptr<Ui::Text::CustomEmoji> {
.repaint = [=] { raw->update(); },
});
auto context = simpleContext;
context.customEmojiFactory = [=](
QStringView data,
const Ui::Text::MarkedContext &context
) -> std::unique_ptr<Ui::Text::CustomEmoji> {
const auto id = Data::ParseCustomEmojiData(data); const auto id = Data::ParseCustomEmojiData(data);
auto result = Ui::Text::MakeCustomEmoji(data, simpleContext); auto result = owner->customEmojiManager().create(
data,
std::move(update));
if (state->unifiedFactoryOwner->lookupReactionId(id).custom()) { if (state->unifiedFactoryOwner->lookupReactionId(id).custom()) {
return std::make_unique<MaybeDisabledEmoji>( return std::make_unique<MaybeDisabledEmoji>(
std::move(result), std::move(result),
@ -382,10 +377,12 @@ object_ptr<Ui::RpWidget> AddReactionsSelector(
using namespace Ui::Text; using namespace Ui::Text;
return std::make_unique<FirstFrameEmoji>(std::move(result)); return std::make_unique<FirstFrameEmoji>(std::move(result));
}; };
raw->setCustomTextContext( raw->setCustomTextContext([=](Fn<void()> repaint) {
std::move(context), return std::any(Core::MarkedTextContext{
customEmojiPaused, .session = session,
customEmojiPaused); .customEmojiRepaint = std::move(repaint),
});
}, customEmojiPaused, customEmojiPaused, std::move(factory));
const auto callback = args.callback; const auto callback = args.callback;
const auto isCustom = [=](DocumentId id) { const auto isCustom = [=](DocumentId id) {

View file

@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_credits.h" #include "api/api_credits.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "core/ui_integration.h" // TextContext #include "core/ui_integration.h" // Core::MarkedTextContext.
#include "data/components/credits.h" #include "data/components/credits.h"
#include "data/data_credits.h" #include "data/data_credits.h"
#include "data/data_photo.h" #include "data/data_photo.h"
@ -511,28 +511,32 @@ TextWithEntities CreditsEmoji(not_null<Main::Session*> session) {
} }
TextWithEntities CreditsEmojiSmall(not_null<Main::Session*> session) { TextWithEntities CreditsEmojiSmall(not_null<Main::Session*> session) {
return Ui::Text::IconEmoji( return Ui::Text::SingleCustomEmoji(
&st::starIconEmoji, session->data().customEmojiManager().registerInternalEmoji(
st::starIconSmall,
st::starIconSmallPadding,
true),
QString(QChar(0x2B50))); QString(QChar(0x2B50)));
} }
not_null<FlatLabel*> SetButtonMarkedLabel( not_null<FlatLabel*> SetButtonMarkedLabel(
not_null<RpWidget*> button, not_null<RpWidget*> button,
rpl::producer<TextWithEntities> text, rpl::producer<TextWithEntities> text,
Text::MarkedContext context, Fn<std::any(Fn<void()> update)> context,
const style::FlatLabel &st, const style::FlatLabel &st,
const style::color *textFg) { const style::color *textFg) {
const auto buttonLabel = Ui::CreateChild<Ui::FlatLabel>( const auto buttonLabel = Ui::CreateChild<Ui::FlatLabel>(
button, button,
rpl::single(QString()), rpl::single(QString()),
st); st);
context.repaint = [=] { buttonLabel->update(); };
rpl::duplicate( rpl::duplicate(
text text
) | rpl::filter([=](const TextWithEntities &text) { ) | rpl::filter([=](const TextWithEntities &text) {
return !text.text.isEmpty(); return !text.text.isEmpty();
}) | rpl::start_with_next([=](const TextWithEntities &text) { }) | rpl::start_with_next([=](const TextWithEntities &text) {
buttonLabel->setMarkedText(text, context); buttonLabel->setMarkedText(
text,
context([=] { buttonLabel->update(); }));
}, buttonLabel->lifetime()); }, buttonLabel->lifetime());
if (textFg) { if (textFg) {
buttonLabel->setTextColorOverride((*textFg)->c); buttonLabel->setTextColorOverride((*textFg)->c);
@ -561,12 +565,15 @@ not_null<FlatLabel*> SetButtonMarkedLabel(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const style::FlatLabel &st, const style::FlatLabel &st,
const style::color *textFg) { const style::color *textFg) {
return SetButtonMarkedLabel(button, text, Core::TextContext({ return SetButtonMarkedLabel(button, text, [=](Fn<void()> update) {
.session = session, return Core::MarkedTextContext{
}), st, textFg); .session = session,
.customEmojiRepaint = update,
};
}, st, textFg);
} }
void SendStarsForm( void SendStarGift(
not_null<Main::Session*> session, not_null<Main::Session*> session,
std::shared_ptr<Payments::CreditsFormData> data, std::shared_ptr<Payments::CreditsFormData> data,
Fn<void(std::optional<QString>)> done) { Fn<void(std::optional<QString>)> done) {

View file

@ -41,7 +41,7 @@ void SendCreditsBox(
not_null<FlatLabel*> SetButtonMarkedLabel( not_null<FlatLabel*> SetButtonMarkedLabel(
not_null<RpWidget*> button, not_null<RpWidget*> button,
rpl::producer<TextWithEntities> text, rpl::producer<TextWithEntities> text,
Text::MarkedContext context, Fn<std::any(Fn<void()> update)> context,
const style::FlatLabel &st, const style::FlatLabel &st,
const style::color *textFg = nullptr); const style::color *textFg = nullptr);
@ -52,7 +52,7 @@ not_null<FlatLabel*> SetButtonMarkedLabel(
const style::FlatLabel &st, const style::FlatLabel &st,
const style::color *textFg = nullptr); const style::color *textFg = nullptr);
void SendStarsForm( void SendStarGift(
not_null<Main::Session*> session, not_null<Main::Session*> session,
std::shared_ptr<Payments::CreditsFormData> data, std::shared_ptr<Payments::CreditsFormData> data,
Fn<void(std::optional<QString>)> done); Fn<void(std::optional<QString>)> done);

View file

@ -59,9 +59,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h" #include "styles/style_chat_helpers.h"
#include "styles/style_layers.h"
#include <QtCore/QMimeData> #include <QtCore/QMimeData>
@ -722,18 +722,6 @@ void SendFilesBox::openDialogToAddFileToAlbum() {
crl::guard(this, callback)); crl::guard(this, callback));
} }
void SendFilesBox::refreshMessagesCount() {
const auto way = _sendWay.current();
const auto withCaption = _list.canAddCaption(
way.groupFiles() && way.sendImagesAsPhotos(),
way.sendImagesAsPhotos());
const auto withComment = !withCaption
&& _caption
&& !_caption->isHidden()
&& !_caption->getTextWithTags().text.isEmpty();
_messagesCount = _list.files.size() + (withComment ? 1 : 0);
}
void SendFilesBox::refreshButtons() { void SendFilesBox::refreshButtons() {
clearButtons(); clearButtons();
@ -742,15 +730,6 @@ void SendFilesBox::refreshButtons() {
? tr::lng_send_button() ? tr::lng_send_button()
: tr::lng_create_group_next()), : tr::lng_create_group_next()),
[=] { send({}); }); [=] { send({}); });
refreshMessagesCount();
const auto perMessage = _captionToPeer
? _captionToPeer->starsPerMessageChecked()
: 0;
if (perMessage > 0) {
_send->setText(PaidSendButtonText(_messagesCount.value(
) | rpl::map(rpl::mappers::_1 * perMessage)));
}
if (_sendType == Api::SendType::Normal) { if (_sendType == Api::SendType::Normal) {
SendMenu::SetupMenuAndShortcuts( SendMenu::SetupMenuAndShortcuts(
_send, _send,
@ -867,9 +846,10 @@ void SendFilesBox::refreshPriceTag() {
QString(), QString(),
st::paidTagLabel); st::paidTagLabel);
std::move(text) | rpl::start_with_next([=](TextWithEntities &&text) { std::move(text) | rpl::start_with_next([=](TextWithEntities &&text) {
label->setMarkedText(text, Core::TextContext({ label->setMarkedText(text, Core::MarkedTextContext{
.session = session, .session = session,
})); .customEmojiRepaint = [=] { label->update(); },
});
}, label->lifetime()); }, label->lifetime());
label->show(); label->show();
label->sizeValue() | rpl::start_with_next([=](QSize size) { label->sizeValue() | rpl::start_with_next([=](QSize size) {
@ -1509,7 +1489,6 @@ void SendFilesBox::setupCaption() {
_caption->changes() _caption->changes()
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
checkCharsLimitation(); checkCharsLimitation();
refreshMessagesCount();
}, _caption->lifetime()); }, _caption->lifetime());
} }

View file

@ -246,7 +246,6 @@ private:
void addPreparedAsyncFile(Ui::PreparedFile &&file); void addPreparedAsyncFile(Ui::PreparedFile &&file);
void checkCharsLimitation(); void checkCharsLimitation();
void refreshMessagesCount();
[[nodiscard]] Fn<MenuDetails()> prepareSendMenuDetails( [[nodiscard]] Fn<MenuDetails()> prepareSendMenuDetails(
const SendFilesBoxDescriptor &descriptor); const SendFilesBoxDescriptor &descriptor);
@ -262,7 +261,6 @@ private:
Ui::PreparedList _list; Ui::PreparedList _list;
std::optional<int> _removingIndex; std::optional<int> _removingIndex;
rpl::variable<int> _messagesCount;
SendFilesLimits _limits = {}; SendFilesLimits _limits = {};
Fn<MenuDetails()> _sendMenuDetails; Fn<MenuDetails()> _sendMenuDetails;

View file

@ -123,13 +123,12 @@ private:
Ui::RoundImageCheckbox checkbox; Ui::RoundImageCheckbox checkbox;
Ui::Text::String name; Ui::Text::String name;
Ui::Animations::Simple nameActive; Ui::Animations::Simple nameActive;
Api::MessageMoneyRestriction restriction; bool locked = false;
RestrictionBadgeCache badgeCache;
}; };
void invalidateCache(); void invalidateCache();
bool showLockedError(not_null<Chat*> chat); bool showLockedError(not_null<Chat*> chat);
void refreshRestrictedRows(); void refreshLockedRows();
[[nodiscard]] int displayedChatsCount() const; [[nodiscard]] int displayedChatsCount() const;
[[nodiscard]] not_null<Data::Thread*> chatThread( [[nodiscard]] not_null<Data::Thread*> chatThread(
@ -138,7 +137,7 @@ private:
void paintChat(Painter &p, not_null<Chat*> chat, int index); void paintChat(Painter &p, not_null<Chat*> chat, int index);
void updateChat(not_null<PeerData*> peer); void updateChat(not_null<PeerData*> peer);
void updateChatName(not_null<Chat*> chat); void updateChatName(not_null<Chat*> chat);
void initChatRestriction(not_null<Chat*> chat); void initChatLocked(not_null<Chat*> chat);
void repaintChat(not_null<PeerData*> peer); void repaintChat(not_null<PeerData*> peer);
int chatIndex(not_null<PeerData*> peer) const; int chatIndex(not_null<PeerData*> peer) const;
void repaintChatAtIndex(int index); void repaintChatAtIndex(int index);
@ -518,19 +517,9 @@ void ShareBox::keyPressEvent(QKeyEvent *e) {
SendMenu::Details ShareBox::sendMenuDetails() const { SendMenu::Details ShareBox::sendMenuDetails() const {
const auto selected = _inner->selected(); const auto selected = _inner->selected();
const auto hasPaid = [&] { const auto type = ranges::all_of(
for (const auto &thread : selected) { selected | ranges::views::transform(&Data::Thread::peer),
if (thread->peer()->starsPerMessageChecked()) { HistoryView::CanScheduleUntilOnline)
return true;
}
}
return false;
}();
const auto type = hasPaid
? SendMenu::Type::SilentOnly
: ranges::all_of(
selected | ranges::views::transform(&Data::Thread::peer),
HistoryView::CanScheduleUntilOnline)
? SendMenu::Type::ScheduledToUser ? SendMenu::Type::ScheduledToUser
: (selected.size() == 1 && selected.front()->peer()->isSelf()) : (selected.size() == 1 && selected.front()->peer()->isSelf())
? SendMenu::Type::Reminder ? SendMenu::Type::Reminder
@ -625,9 +614,6 @@ void ShareBox::createButtons() {
showMenu(send); showMenu(send);
} }
}, send->lifetime()); }, send->lifetime());
send->setText(PaidSendButtonText(
_starsToSend.value(),
tr::lng_share_confirm()));
} else if (_descriptor.copyCallback) { } else if (_descriptor.copyCallback) {
addButton(_copyLinkText.value(), [=] { copyLink(); }); addButton(_copyLinkText.value(), [=] { copyLink(); });
} }
@ -671,73 +657,6 @@ void ShareBox::innerSelectedChanged(
} }
void ShareBox::submit(Api::SendOptions options) { void ShareBox::submit(Api::SendOptions options) {
_submitLifetime.destroy();
auto threads = _inner->selected();
const auto weak = Ui::MakeWeak(this);
const auto field = _comment->entity();
auto comment = field->getTextWithAppliedMarkdown();
const auto checkPaid = [=] {
if (!_descriptor.countMessagesCallback) {
return true;
}
const auto withPaymentApproved = crl::guard(weak, [=](int approved) {
auto copy = options;
copy.starsApproved = approved;
submit(copy);
});
const auto messagesCount = _descriptor.countMessagesCallback(
comment);
const auto alreadyApproved = options.starsApproved;
auto paid = std::vector<not_null<PeerData*>>();
auto waiting = base::flat_set<not_null<PeerData*>>();
auto totalStars = 0;
for (const auto &thread : threads) {
const auto peer = thread->peer();
const auto details = ComputePaymentDetails(peer, messagesCount);
if (!details) {
waiting.emplace(peer);
} else if (details->stars > 0) {
totalStars += details->stars;
paid.push_back(peer);
}
}
if (!waiting.empty()) {
_descriptor.session->changes().peerUpdates(
Data::PeerUpdate::Flag::FullInfo
) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
if (waiting.contains(update.peer)) {
withPaymentApproved(alreadyApproved);
}
}, _submitLifetime);
if (!_descriptor.session->credits().loaded()) {
_descriptor.session->credits().loadedValue(
) | rpl::filter(
rpl::mappers::_1
) | rpl::take(1) | rpl::start_with_next([=] {
withPaymentApproved(alreadyApproved);
}, _submitLifetime);
}
return false;
} else if (totalStars > alreadyApproved) {
const auto show = uiShow();
const auto session = _descriptor.session;
const auto sessionShow = Main::MakeSessionShow(show, session);
const auto scheduleBoxSt = _descriptor.st.scheduleBox.get();
ShowSendPaidConfirm(sessionShow, paid, SendPaymentDetails{
.messages = messagesCount,
.stars = totalStars,
}, [=] { withPaymentApproved(totalStars); }, PaidConfirmStyles{
.label = (scheduleBoxSt
? scheduleBoxSt->chooseDateTimeArgs.labelStyle
: nullptr),
.checkbox = _descriptor.st.checkbox,
});
return false;
}
return true;
};
if (const auto onstack = _descriptor.submitCallback) { if (const auto onstack = _descriptor.submitCallback) {
const auto forwardOptions = (_forwardOptions.captionsCount const auto forwardOptions = (_forwardOptions.captionsCount
&& _forwardOptions.dropCaptions) && _forwardOptions.dropCaptions)
@ -746,9 +665,8 @@ void ShareBox::submit(Api::SendOptions options) {
? Data::ForwardOptions::NoSenderNames ? Data::ForwardOptions::NoSenderNames
: Data::ForwardOptions::PreserveInfo; : Data::ForwardOptions::PreserveInfo;
onstack( onstack(
std::move(threads), _inner->selected(),
checkPaid, _comment->entity()->getTextWithAppliedMarkdown(),
std::move(comment),
options, options,
forwardOptions); forwardOptions);
} }
@ -768,23 +686,9 @@ void ShareBox::selectedChanged() {
_comment->toggle(_hasSelected, anim::type::normal); _comment->toggle(_hasSelected, anim::type::normal);
_comment->resizeToWidth(st::boxWideWidth); _comment->resizeToWidth(st::boxWideWidth);
} }
computeStarsCount();
update(); update();
} }
void ShareBox::computeStarsCount() {
auto perMessage = 0;
for (const auto &thread : _inner->selected()) {
perMessage += thread->peer()->starsPerMessageChecked();
}
const auto messagesCount = _descriptor.countMessagesCallback
? _descriptor.countMessagesCallback(_comment
? _comment->entity()->getTextWithTags()
: TextWithTags())
: 0;
_starsToSend = perMessage * messagesCount;
}
void ShareBox::scrollTo(Ui::ScrollToRequest request) { void ShareBox::scrollTo(Ui::ScrollToRequest request) {
scrollToY(request.ymin, request.ymax); scrollToY(request.ymin, request.ymax);
//auto scrollTop = scrollArea()->scrollTop(), scrollBottom = scrollTop + scrollArea()->height(); //auto scrollTop = scrollArea()->scrollTop(), scrollBottom = scrollTop + scrollArea()->height();
@ -822,13 +726,13 @@ ShareBox::Inner::Inner(
_rowHeight = st::shareRowHeight; _rowHeight = st::shareRowHeight;
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);
if (_descriptor.moneyRestrictionError) { if (_descriptor.premiumRequiredError) {
const auto session = _descriptor.session; const auto session = _descriptor.session;
rpl::merge( rpl::merge(
Data::AmPremiumValue(session) | rpl::to_empty, Data::AmPremiumValue(session) | rpl::to_empty,
session->api().premium().someMessageMoneyRestrictionsResolved() session->api().premium().somePremiumRequiredResolved()
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
refreshRestrictedRows(); refreshLockedRows();
}, lifetime()); }, lifetime());
} }
@ -889,36 +793,38 @@ void ShareBox::Inner::invalidateCache() {
} }
bool ShareBox::Inner::showLockedError(not_null<Chat*> chat) { bool ShareBox::Inner::showLockedError(not_null<Chat*> chat) {
if (!chat->restriction.premiumRequired) { if (!chat->locked) {
return false; return false;
} }
::Settings::ShowPremiumPromoToast( ::Settings::ShowPremiumPromoToast(
Main::MakeSessionShow(_show, _descriptor.session), Main::MakeSessionShow(_show, _descriptor.session),
ChatHelpers::ResolveWindowDefault(), ChatHelpers::ResolveWindowDefault(),
_descriptor.moneyRestrictionError(chat->peer->asUser()).text, _descriptor.premiumRequiredError(chat->peer->asUser()).text,
u"require_premium"_q); u"require_premium"_q);
return true; return true;
} }
void ShareBox::Inner::refreshRestrictedRows() { void ShareBox::Inner::refreshLockedRows() {
auto changed = false; auto changed = false;
for (const auto &[peer, data] : _dataMap) { for (const auto &[peer, data] : _dataMap) {
const auto history = data->history; const auto history = data->history;
const auto restriction = Api::ResolveMessageMoneyRestrictions( const auto locked = (Api::ResolveRequiresPremiumToWrite(
history->peer, history->peer,
history); history
if (data->restriction != restriction) { ) == Api::RequirePremiumState::Yes);
data->restriction = restriction; if (data->locked != locked) {
data->locked = locked;
changed = true; changed = true;
} }
} }
for (const auto &data : d_byUsernameFiltered) { for (const auto &data : d_byUsernameFiltered) {
const auto history = data->history; const auto history = data->history;
const auto restriction = Api::ResolveMessageMoneyRestrictions( const auto locked = (Api::ResolveRequiresPremiumToWrite(
history->peer, history->peer,
history); history
if (data->restriction != restriction) { ) == Api::RequirePremiumState::Yes);
data->restriction = restriction; if (data->locked != locked) {
data->locked = locked;
changed = true; changed = true;
} }
} }
@ -985,14 +891,14 @@ void ShareBox::Inner::updateChatName(not_null<Chat*> chat) {
chat->name.setText(_st.item.nameStyle, text, Ui::NameTextOptions()); chat->name.setText(_st.item.nameStyle, text, Ui::NameTextOptions());
} }
void ShareBox::Inner::initChatRestriction(not_null<Chat*> chat) { void ShareBox::Inner::initChatLocked(not_null<Chat*> chat) {
if (_descriptor.moneyRestrictionError) { if (_descriptor.premiumRequiredError) {
const auto history = chat->history; const auto history = chat->history;
const auto restriction = Api::ResolveMessageMoneyRestrictions( if (Api::ResolveRequiresPremiumToWrite(
history->peer, history->peer,
history); history
if (restriction || restriction.known) { ) == Api::RequirePremiumState::Yes) {
chat->restriction = restriction; chat->locked = true;
} }
} }
} }
@ -1118,15 +1024,14 @@ void ShareBox::Inner::loadProfilePhotos() {
void ShareBox::Inner::preloadUserpic(not_null<Dialogs::Entry*> entry) { void ShareBox::Inner::preloadUserpic(not_null<Dialogs::Entry*> entry) {
entry->chatListPreloadData(); entry->chatListPreloadData();
const auto history = entry->asHistory(); const auto history = entry->asHistory();
if (!_descriptor.moneyRestrictionError || !history) { if (!_descriptor.premiumRequiredError || !history) {
return; return;
} else if (!Api::ResolveMessageMoneyRestrictions( } else if (Api::ResolveRequiresPremiumToWrite(
history->peer, history->peer,
history).known) { history
if (const auto user = history->peer->asUser()) { ) == Api::RequirePremiumState::Unknown) {
const auto api = &_descriptor.session->api(); const auto user = history->peer->asUser();
api->premium().resolveMessageMoneyRestrictions(user); _descriptor.session->api().premium().resolvePremiumRequired(user);
}
} }
} }
@ -1149,7 +1054,7 @@ auto ShareBox::Inner::getChat(not_null<Dialogs::Row*> row)
repaintChat(peer); repaintChat(peer);
})); }));
updateChatName(i->second.get()); updateChatName(i->second.get());
initChatRestriction(i->second.get()); initChatLocked(i->second.get());
row->attached = i->second.get(); row->attached = i->second.get();
return i->second.get(); return i->second.get();
} }
@ -1183,12 +1088,10 @@ void ShareBox::Inner::paintChat(
auto photoTop = st::sharePhotoTop; auto photoTop = st::sharePhotoTop;
chat->checkbox.paint(p, x + photoLeft, y + photoTop, outerWidth); chat->checkbox.paint(p, x + photoLeft, y + photoTop, outerWidth);
if (chat->restriction) { if (chat->locked) {
PaintRestrictionBadge( PaintPremiumRequiredLock(
p, p,
&_st.item, &_st.item,
chat->restriction.starsPerMessage,
chat->badgeCache,
x + photoLeft, x + photoLeft,
y + photoTop, y + photoTop,
outerWidth, outerWidth,
@ -1543,7 +1446,7 @@ void ShareBox::Inner::peopleReceived(
_st.item, _st.item,
[=] { repaintChat(peer); })); [=] { repaintChat(peer); }));
updateChatName(d_byUsernameFiltered.back().get()); updateChatName(d_byUsernameFiltered.back().get());
initChatRestriction(d_byUsernameFiltered.back().get()); initChatLocked(d_byUsernameFiltered.back().get());
} }
} }
}; };
@ -1596,15 +1499,6 @@ ChatHelpers::ForwardedMessagePhraseArgs CreateForwardedMessagePhraseArgs(
}; };
} }
ShareBox::CountMessagesCallback ShareBox::DefaultForwardCountMessages(
not_null<History*> history,
MessageIdsList msgIds) {
return [=](const TextWithTags &comment) {
const auto items = history->owner().idsToItems(msgIds);
return int(items.size()) + (comment.empty() ? 0 : 1);
};
}
ShareBox::SubmitCallback ShareBox::DefaultForwardCallback( ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
std::shared_ptr<Ui::Show> show, std::shared_ptr<Ui::Show> show,
not_null<History*> history, not_null<History*> history,
@ -1616,14 +1510,12 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
const auto state = std::make_shared<State>(); const auto state = std::make_shared<State>();
return [=]( return [=](
std::vector<not_null<Data::Thread*>> &&result, std::vector<not_null<Data::Thread*>> &&result,
Fn<bool()> checkPaid, TextWithTags &&comment,
TextWithTags comment,
Api::SendOptions options, Api::SendOptions options,
Data::ForwardOptions forwardOptions) { Data::ForwardOptions forwardOptions) {
if (!state->requests.empty()) { if (!state->requests.empty()) {
return; // Share clicked already. return; // Share clicked already.
} }
const auto items = history->owner().idsToItems(msgIds); const auto items = history->owner().idsToItems(msgIds);
const auto existingIds = history->owner().itemsToIds(items); const auto existingIds = history->owner().itemsToIds(items);
if (existingIds.empty() || result.empty()) { if (existingIds.empty() || result.empty()) {
@ -1636,8 +1528,6 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
if (error.error) { if (error.error) {
show->showBox(MakeSendErrorBox(error, result.size() > 1)); show->showBox(MakeSendErrorBox(error, result.size() > 1));
return; return;
} else if (!checkPaid()) {
return;
} }
using Flag = MTPmessages_ForwardMessages::Flag; using Flag = MTPmessages_ForwardMessages::Flag;
@ -1686,12 +1576,6 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
: topicRootId; : topicRootId;
const auto peer = thread->peer(); const auto peer = thread->peer();
const auto threadHistory = thread->owningHistory(); const auto threadHistory = thread->owningHistory();
const auto starsPaid = std::min(
peer->starsPerMessageChecked(),
options.starsApproved);
if (starsPaid) {
options.starsApproved -= starsPaid;
}
histories.sendRequest(threadHistory, requestType, [=]( histories.sendRequest(threadHistory, requestType, [=](
Fn<void()> finish) { Fn<void()> finish) {
const auto session = &threadHistory->session(); const auto session = &threadHistory->session();
@ -1703,8 +1587,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
: Flag(0)) : Flag(0))
| (options.shortcutId | (options.shortcutId
? Flag::f_quick_reply_shortcut ? Flag::f_quick_reply_shortcut
: Flag(0)) : Flag(0));
| (starsPaid ? Flag::f_allow_paid_stars : Flag());
threadHistory->sendRequestId = api.request( threadHistory->sendRequestId = api.request(
MTPmessages_ForwardMessages( MTPmessages_ForwardMessages(
MTP_flags(sendFlags), MTP_flags(sendFlags),
@ -1716,8 +1599,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
MTP_int(options.scheduled), MTP_int(options.scheduled),
MTP_inputPeerEmpty(), // send_as MTP_inputPeerEmpty(), // send_as
Data::ShortcutIdToMTP(session, options.shortcutId), Data::ShortcutIdToMTP(session, options.shortcutId),
MTP_int(videoTimestamp.value_or(0)), MTP_int(videoTimestamp.value_or(0))
MTP_long(starsPaid)
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) { )).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
threadHistory->session().api().applyUpdates(updates); threadHistory->session().api().applyUpdates(updates);
state->requests.remove(reqId); state->requests.remove(reqId);
@ -1739,11 +1621,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
finish(); finish();
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {
const auto type = error.type(); if (error.type() == u"VOICE_MESSAGES_FORBIDDEN"_q) {
if (type.startsWith(u"ALLOW_PAYMENT_REQUIRED_"_q)) {
show->showToast(u"Payment requirements changed. "
"Please, try again."_q);
} else if (type == u"VOICE_MESSAGES_FORBIDDEN"_q) {
show->showToast( show->showToast(
tr::lng_restricted_send_voice_messages( tr::lng_restricted_send_voice_messages(
tr::now, tr::now,
@ -1782,7 +1660,6 @@ ShareBoxStyleOverrides DarkShareBoxStyle() {
.comment = &st::groupCallShareBoxComment, .comment = &st::groupCallShareBoxComment,
.peerList = &st::groupCallShareBoxList, .peerList = &st::groupCallShareBoxList,
.label = &st::groupCallField, .label = &st::groupCallField,
.checkbox = &st::groupCallCheckbox,
.scheduleBox = std::make_shared<ScheduleBoxStyleArgs>(schedule()), .scheduleBox = std::make_shared<ScheduleBoxStyleArgs>(schedule()),
}; };
} }
@ -1839,7 +1716,7 @@ void FastShareMessage(
const auto requiresInline = item->requiresSendInlineRight(); const auto requiresInline = item->requiresSendInlineRight();
auto filterCallback = [=](not_null<Data::Thread*> thread) { auto filterCallback = [=](not_null<Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) { if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreMoneyRestrictions()) { if (user->canSendIgnoreRequirePremium()) {
return true; return true;
} }
} }
@ -1854,9 +1731,6 @@ void FastShareMessage(
show->show(Box<ShareBox>(ShareBox::Descriptor{ show->show(Box<ShareBox>(ShareBox::Descriptor{
.session = session, .session = session,
.copyCallback = std::move(copyLinkCallback), .copyCallback = std::move(copyLinkCallback),
.countMessagesCallback = ShareBox::DefaultForwardCountMessages(
history,
msgIds),
.submitCallback = ShareBox::DefaultForwardCallback( .submitCallback = ShareBox::DefaultForwardCallback(
show, show,
history, history,
@ -1868,7 +1742,7 @@ void FastShareMessage(
.captionsCount = ItemsForwardCaptionsCount(items), .captionsCount = ItemsForwardCaptionsCount(items),
.show = !hasOnlyForcedForwardedInfo, .show = !hasOnlyForcedForwardedInfo,
}, },
.moneyRestrictionError = ShareMessageMoneyRestrictionError(), .premiumRequiredError = SharePremiumRequiredError(),
}), Ui::LayerOption::CloseOther); }), Ui::LayerOption::CloseOther);
} }
@ -1896,12 +1770,8 @@ void FastShareLink(
QGuiApplication::clipboard()->setText(url); QGuiApplication::clipboard()->setText(url);
show->showToast(tr::lng_background_link_copied(tr::now)); show->showToast(tr::lng_background_link_copied(tr::now));
}; };
auto countMessagesCallback = [=](const TextWithTags &comment) {
return 1;
};
auto submitCallback = [=]( auto submitCallback = [=](
std::vector<not_null<::Data::Thread*>> &&result, std::vector<not_null<::Data::Thread*>> &&result,
Fn<bool()> checkPaid,
TextWithTags &&comment, TextWithTags &&comment,
Api::SendOptions options, Api::SendOptions options,
::Data::ForwardOptions) { ::Data::ForwardOptions) {
@ -1918,8 +1788,6 @@ void FastShareLink(
MakeSendErrorBox(error, result.size() > 1)); MakeSendErrorBox(error, result.size() > 1));
} }
return; return;
} else if (!checkPaid()) {
return;
} }
*sending = true; *sending = true;
@ -1947,7 +1815,7 @@ void FastShareLink(
}; };
auto filterCallback = [](not_null<::Data::Thread*> thread) { auto filterCallback = [](not_null<::Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) { if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreMoneyRestrictions()) { if (user->canSendIgnoreRequirePremium()) {
return true; return true;
} }
} }
@ -1957,17 +1825,16 @@ void FastShareLink(
Box<ShareBox>(ShareBox::Descriptor{ Box<ShareBox>(ShareBox::Descriptor{
.session = &show->session(), .session = &show->session(),
.copyCallback = std::move(copyCallback), .copyCallback = std::move(copyCallback),
.countMessagesCallback = std::move(countMessagesCallback),
.submitCallback = std::move(submitCallback), .submitCallback = std::move(submitCallback),
.filterCallback = std::move(filterCallback), .filterCallback = std::move(filterCallback),
.st = st, .st = st,
.moneyRestrictionError = ShareMessageMoneyRestrictionError(), .premiumRequiredError = SharePremiumRequiredError(),
}), }),
Ui::LayerOption::KeepOther, Ui::LayerOption::KeepOther,
anim::type::normal); anim::type::normal);
} }
auto ShareMessageMoneyRestrictionError() auto SharePremiumRequiredError()
-> Fn<RecipientMoneyRestrictionError(not_null<UserData*>)> { -> Fn<RecipientPremiumRequiredError(not_null<UserData*>)> {
return WriteMoneyRestrictionError; return WritePremiumRequiredError;
} }

View file

@ -66,7 +66,6 @@ struct ShareBoxStyleOverrides {
const style::InputField *comment = nullptr; const style::InputField *comment = nullptr;
const style::PeerList *peerList = nullptr; const style::PeerList *peerList = nullptr;
const style::InputField *label = nullptr; const style::InputField *label = nullptr;
const style::Checkbox *checkbox = nullptr;
std::shared_ptr<HistoryView::ScheduleBoxStyleArgs> scheduleBox; std::shared_ptr<HistoryView::ScheduleBoxStyleArgs> scheduleBox;
}; };
[[nodiscard]] ShareBoxStyleOverrides DarkShareBoxStyle(); [[nodiscard]] ShareBoxStyleOverrides DarkShareBoxStyle();
@ -88,25 +87,20 @@ void FastShareLink(
const QString &url, const QString &url,
ShareBoxStyleOverrides st = {}); ShareBoxStyleOverrides st = {});
struct RecipientMoneyRestrictionError; struct RecipientPremiumRequiredError;
[[nodiscard]] auto ShareMessageMoneyRestrictionError() [[nodiscard]] auto SharePremiumRequiredError()
-> Fn<RecipientMoneyRestrictionError(not_null<UserData*>)>; -> Fn<RecipientPremiumRequiredError(not_null<UserData*>)>;
class ShareBox final : public Ui::BoxContent { class ShareBox final : public Ui::BoxContent {
public: public:
using CopyCallback = Fn<void()>; using CopyCallback = Fn<void()>;
using CountMessagesCallback = Fn<int(const TextWithTags&)>;
using SubmitCallback = Fn<void( using SubmitCallback = Fn<void(
std::vector<not_null<Data::Thread*>>&&, std::vector<not_null<Data::Thread*>>&&,
Fn<bool()> checkPaid,
TextWithTags&&, TextWithTags&&,
Api::SendOptions, Api::SendOptions,
Data::ForwardOptions)>; Data::ForwardOptions)>;
using FilterCallback = Fn<bool(not_null<Data::Thread*>)>; using FilterCallback = Fn<bool(not_null<Data::Thread*>)>;
[[nodiscard]] static auto DefaultForwardCountMessages(
not_null<History*> history,
MessageIdsList msgIds) -> CountMessagesCallback;
[[nodiscard]] static SubmitCallback DefaultForwardCallback( [[nodiscard]] static SubmitCallback DefaultForwardCallback(
std::shared_ptr<Ui::Show> show, std::shared_ptr<Ui::Show> show,
not_null<History*> history, not_null<History*> history,
@ -116,7 +110,6 @@ public:
struct Descriptor { struct Descriptor {
not_null<Main::Session*> session; not_null<Main::Session*> session;
CopyCallback copyCallback; CopyCallback copyCallback;
CountMessagesCallback countMessagesCallback;
SubmitCallback submitCallback; SubmitCallback submitCallback;
FilterCallback filterCallback; FilterCallback filterCallback;
object_ptr<Ui::RpWidget> bottomWidget = { nullptr }; object_ptr<Ui::RpWidget> bottomWidget = { nullptr };
@ -130,9 +123,8 @@ public:
bool show = false; bool show = false;
} forwardOptions; } forwardOptions;
using MoneyRestrictionError = RecipientMoneyRestrictionError; using PremiumRequiredError = RecipientPremiumRequiredError;
Fn<MoneyRestrictionError( Fn<PremiumRequiredError(not_null<UserData*>)> premiumRequiredError;
not_null<UserData*>)> moneyRestrictionError;
}; };
ShareBox(QWidget*, Descriptor &&descriptor); ShareBox(QWidget*, Descriptor &&descriptor);
@ -157,7 +149,6 @@ private:
void needSearchByUsername(); void needSearchByUsername();
void applyFilterUpdate(const QString &query); void applyFilterUpdate(const QString &query);
void selectedChanged(); void selectedChanged();
void computeStarsCount();
void createButtons(); void createButtons();
int getTopScrollSkip() const; int getTopScrollSkip() const;
int getBottomScrollSkip() const; int getBottomScrollSkip() const;
@ -189,7 +180,6 @@ private:
bool _hasSelected = false; bool _hasSelected = false;
rpl::variable<QString> _copyLinkText; rpl::variable<QString> _copyLinkText;
rpl::variable<int> _starsToSend;
base::Timer _searchTimer; base::Timer _searchTimer;
QString _peopleQuery; QString _peopleQuery;
@ -205,6 +195,5 @@ private:
PeopleQueries _peopleQueries; PeopleQueries _peopleQueries;
Ui::Animations::Simple _scrollAnimation; Ui::Animations::Simple _scrollAnimation;
rpl::lifetime _submitLifetime;
}; };

View file

@ -27,7 +27,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/tabbed_panel.h" #include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h" #include "chat_helpers/tabbed_selector.h"
#include "core/ui_integration.h" #include "core/ui_integration.h"
#include "data/data_changes.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_credits.h" #include "data/data_credits.h"
#include "data/data_document.h" #include "data/data_document.h"
@ -81,7 +80,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/checkbox.h" #include "ui/widgets/checkbox.h"
#include "ui/widgets/popup_menu.h" #include "ui/widgets/popup_menu.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.h"
#include "ui/wrap/slide_wrap.h"
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
#include "window/section_widget.h" #include "window/section_widget.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
@ -108,7 +106,6 @@ constexpr auto kSentToastDuration = 3 * crl::time(1000);
constexpr auto kSwitchUpgradeCoverInterval = 3 * crl::time(1000); constexpr auto kSwitchUpgradeCoverInterval = 3 * crl::time(1000);
constexpr auto kCrossfadeDuration = crl::time(400); constexpr auto kCrossfadeDuration = crl::time(400);
constexpr auto kUpgradeDoneToastDuration = 4 * crl::time(1000); constexpr auto kUpgradeDoneToastDuration = 4 * crl::time(1000);
constexpr auto kGiftsPreloadTimeout = 3 * crl::time(1000);
using namespace HistoryView; using namespace HistoryView;
using namespace Info::PeerGifts; using namespace Info::PeerGifts;
@ -129,7 +126,6 @@ struct GiftDetails {
uint64 randomId = 0; uint64 randomId = 0;
bool anonymous = false; bool anonymous = false;
bool upgraded = false; bool upgraded = false;
bool byStars = false;
}; };
class PreviewDelegate final : public DefaultElementDelegate { class PreviewDelegate final : public DefaultElementDelegate {
@ -231,7 +227,7 @@ auto GenerateGiftMedia(
TextWithEntities text, TextWithEntities text,
QMargins margins = {}, QMargins margins = {},
const base::flat_map<uint16, ClickHandlerPtr> &links = {}, const base::flat_map<uint16, ClickHandlerPtr> &links = {},
Ui::Text::MarkedContext context = {}) { const std::any &context = {}) {
if (text.empty()) { if (text.empty()) {
return; return;
} }
@ -240,7 +236,7 @@ auto GenerateGiftMedia(
margins, margins,
st::defaultTextStyle, st::defaultTextStyle,
links, links,
std::move(context))); context));
}; };
const auto sticker = [=] { const auto sticker = [=] {
@ -310,10 +306,10 @@ auto GenerateGiftMedia(
auto description = data.text.empty() auto description = data.text.empty()
? std::move(textFallback) ? std::move(textFallback)
: data.text; : data.text;
const auto context = Core::TextContext({ const auto context = Core::MarkedTextContext{
.session = &parent->history()->session(), .session = &parent->history()->session(),
.repaint = [parent] { parent->repaint(); }, .customEmojiRepaint = [parent] { parent->repaint(); },
}); };
pushText( pushText(
std::move(title), std::move(title),
st::giftBoxPreviewTitlePadding, st::giftBoxPreviewTitlePadding,
@ -499,14 +495,7 @@ void PreviewWrap::prepare(rpl::producer<GiftDetails> details) {
std::move(details) | rpl::start_with_next([=](GiftDetails details) { std::move(details) | rpl::start_with_next([=](GiftDetails details) {
const auto &descriptor = details.descriptor; const auto &descriptor = details.descriptor;
const auto cost = v::match(descriptor, [&](GiftTypePremium data) { const auto cost = v::match(descriptor, [&](GiftTypePremium data) {
const auto stars = (details.byStars && data.stars) return FillAmountAndCurrency(data.cost, data.currency, true);
? data.stars
: (data.currency == kCreditsCurrency)
? data.cost
: 0;
return stars
? tr::lng_gift_stars_title(tr::now, lt_count, stars)
: FillAmountAndCurrency(data.cost, data.currency, true);
}, [&](GiftTypeStars data) { }, [&](GiftTypeStars data) {
const auto stars = data.info.stars const auto stars = data.info.stars
+ (details.upgraded ? data.info.starsToUpgrade : 0); + (details.upgraded ? data.info.starsToUpgrade : 0);
@ -633,27 +622,14 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
list.reserve(options.size()); list.reserve(options.size());
auto minMonthsGift = GiftTypePremium(); auto minMonthsGift = GiftTypePremium();
for (const auto &option : options) { for (const auto &option : options) {
if (option.currency != kCreditsCurrency) { list.push_back({
list.push_back({ .cost = option.cost,
.cost = option.cost, .currency = option.currency,
.currency = option.currency, .months = option.months,
.months = option.months, });
}); if (!minMonthsGift.months
if (!minMonthsGift.months || option.months < minMonthsGift.months) {
|| option.months < minMonthsGift.months) { minMonthsGift = list.back();
minMonthsGift = list.back();
}
}
}
for (const auto &option : options) {
if (option.currency == kCreditsCurrency) {
const auto i = ranges::find(
list,
option.months,
&GiftTypePremium::months);
if (i != end(list)) {
i->stars = option.cost;
}
} }
} }
for (auto &gift : list) { for (auto &gift : list) {
@ -759,11 +735,15 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
} }
auto &manager = session->data().customEmojiManager(); auto &manager = session->data().customEmojiManager();
auto result = Text::String(); auto result = Text::String();
const auto context = Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [] {},
};
result.setMarkedText( result.setMarkedText(
st::semiboldTextStyle, st::semiboldTextStyle,
manager.creditsEmoji().append(QString::number(price)), manager.creditsEmoji().append(QString::number(price)),
kMarkupTextOptions, kMarkupTextOptions,
Core::TextContext({ .session = session })); context);
return result; return result;
} }
@ -1123,35 +1103,16 @@ void SendGift(
std::shared_ptr<Api::PremiumGiftCodeOptions> api, std::shared_ptr<Api::PremiumGiftCodeOptions> api,
const GiftDetails &details, const GiftDetails &details,
Fn<void(Payments::CheckoutResult)> done) { Fn<void(Payments::CheckoutResult)> done) {
const auto processNonPanelPaymentFormFactory
= Payments::ProcessNonPanelPaymentFormFactory(window, done);
v::match(details.descriptor, [&](const GiftTypePremium &gift) { v::match(details.descriptor, [&](const GiftTypePremium &gift) {
if (details.byStars && gift.stars) { auto invoice = api->invoice(1, gift.months);
auto invoice = Payments::InvoicePremiumGiftCode{ invoice.purpose = Payments::InvoicePremiumGiftCodeUsers{
.purpose = Payments::InvoicePremiumGiftCodeUsers{ .users = { peer->asUser() },
.users = { peer->asUser() }, .message = details.text,
.message = details.text, };
}, Payments::CheckoutProcess::Start(std::move(invoice), done);
.currency = Ui::kCreditsCurrency,
.randomId = details.randomId,
.amount = uint64(gift.stars),
.storeQuantity = 1,
.users = 1,
.months = gift.months,
};
Payments::CheckoutProcess::Start(
std::move(invoice),
done,
processNonPanelPaymentFormFactory);
} else {
auto invoice = api->invoice(1, gift.months);
invoice.purpose = Payments::InvoicePremiumGiftCodeUsers{
.users = { peer->asUser() },
.message = details.text,
};
Payments::CheckoutProcess::Start(std::move(invoice), done);
}
}, [&](const GiftTypeStars &gift) { }, [&](const GiftTypeStars &gift) {
const auto processNonPanelPaymentFormFactory
= Payments::ProcessNonPanelPaymentFormFactory(window, done);
Payments::CheckoutProcess::Start(Payments::InvoiceStarGift{ Payments::CheckoutProcess::Start(Payments::InvoiceStarGift{
.giftId = gift.info.id, .giftId = gift.info.id,
.randomId = details.randomId, .randomId = details.randomId,
@ -1318,6 +1279,12 @@ void AddUpgradeButton(
button->toggleOn(rpl::single(false))->toggledValue( button->toggleOn(rpl::single(false))->toggledValue(
) | rpl::start_with_next(toggled, button->lifetime()); ) | rpl::start_with_next(toggled, button->lifetime());
const auto makeContext = [session](Fn<void()> update) {
return Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = std::move(update),
};
};
auto star = session->data().customEmojiManager().creditsEmoji(); auto star = session->data().customEmojiManager().creditsEmoji();
const auto label = Ui::CreateChild<Ui::FlatLabel>( const auto label = Ui::CreateChild<Ui::FlatLabel>(
button, button,
@ -1329,7 +1296,7 @@ void AddUpgradeButton(
Text::WithEntities), Text::WithEntities),
st::boxLabel, st::boxLabel,
st::defaultPopupMenu, st::defaultPopupMenu,
Core::TextContext({ .session = session })); std::move(makeContext));
label->show(); label->show();
label->setAttribute(Qt::WA_TransparentForMouseEvents); label->setAttribute(Qt::WA_TransparentForMouseEvents);
button->widthValue() | rpl::start_with_next([=](int outer) { button->widthValue() | rpl::start_with_next([=](int outer) {
@ -1457,7 +1424,6 @@ void SendGiftBox(
struct State { struct State {
rpl::variable<GiftDetails> details; rpl::variable<GiftDetails> details;
rpl::variable<bool> messageAllowed;
std::shared_ptr<Data::DocumentMedia> media; std::shared_ptr<Data::DocumentMedia> media;
bool submitting = false; bool submitting = false;
}; };
@ -1466,25 +1432,13 @@ void SendGiftBox(
.descriptor = descriptor, .descriptor = descriptor,
.randomId = base::RandomValue<uint64>(), .randomId = base::RandomValue<uint64>(),
}; };
peer->updateFull();
state->messageAllowed = peer->session().changes().peerFlagsValue(
peer,
Data::PeerUpdate::Flag::StarsPerMessage
) | rpl::map([=] {
return peer->starsPerMessageChecked() == 0;
});
auto cost = state->details.value( auto cost = state->details.value(
) | rpl::map([session](const GiftDetails &details) { ) | rpl::map([session](const GiftDetails &details) {
return v::match(details.descriptor, [&](const GiftTypePremium &data) { return v::match(details.descriptor, [&](const GiftTypePremium &data) {
const auto stars = (details.byStars && data.stars) if (data.currency == kCreditsCurrency) {
? data.stars
: (data.currency == kCreditsCurrency)
? data.cost
: 0;
if (stars) {
return CreditsEmojiSmall(session).append( return CreditsEmojiSmall(session).append(
Lang::FormatCountDecimal(std::abs(stars))); Lang::FormatCountDecimal(std::abs(data.cost)));
} }
return TextWithEntities{ return TextWithEntities{
FillAmountAndCurrency(data.cost, data.currency), FillAmountAndCurrency(data.cost, data.currency),
@ -1508,17 +1462,10 @@ void SendGiftBox(
peer, peer,
state->details.value())); state->details.value()));
const auto messageWrap = container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container,
object_ptr<Ui::VerticalLayout>(container)));
messageWrap->toggleOn(state->messageAllowed.value());
messageWrap->finishAnimating();
const auto messageInner = messageWrap->entity();
const auto limit = StarGiftMessageLimit(session); const auto limit = StarGiftMessageLimit(session);
const auto text = AddPartInput( const auto text = AddPartInput(
window, window,
messageInner, container,
box->getDelegate()->outerContainer(), box->getDelegate()->outerContainer(),
tr::lng_gift_send_message(), tr::lng_gift_send_message(),
QString(), QString(),
@ -1562,6 +1509,7 @@ void SendGiftBox(
text, text,
session, session,
{ .suggestCustomEmoji = true, .allowCustomWithoutPremium = allow }); { .suggestCustomEmoji = true, .allowCustomWithoutPremium = allow });
if (stars) { if (stars) {
const auto cost = stars->info.starsToUpgrade; const auto cost = stars->info.starsToUpgrade;
if (cost > 0 && !peer->isSelf()) { if (cost > 0 && !peer->isSelf()) {
@ -1603,73 +1551,20 @@ void SendGiftBox(
}, container->lifetime()); }, container->lifetime());
AddSkip(container); AddSkip(container);
} }
v::match(descriptor, [&](const GiftTypePremium &data) { v::match(descriptor, [&](const GiftTypePremium &) {
AddDividerText(messageInner, tr::lng_gift_send_premium_about( AddDividerText(container, tr::lng_gift_send_premium_about(
lt_user, lt_user,
rpl::single(peer->shortName()))); rpl::single(peer->shortName())));
if (const auto byStars = data.stars) {
const auto star = Ui::Text::IconEmoji(&st::starIconEmojiColored);
AddSkip(container);
container->add(
object_ptr<SettingsButton>(
container,
tr::lng_gift_send_pay_with_stars(
lt_amount,
rpl::single(base::duplicate(star).append(Lang::FormatCountDecimal(byStars))),
Ui::Text::WithEntities),
st::settingsButtonNoIcon)
)->toggleOn(rpl::single(false))->toggledValue(
) | rpl::start_with_next([=](bool toggled) {
auto now = state->details.current();
now.byStars = toggled;
state->details = std::move(now);
}, container->lifetime());
AddSkip(container);
const auto balance = AddDividerText(
container,
tr::lng_gift_send_stars_balance(
lt_amount,
peer->session().credits().balanceValue(
) | rpl::map([=](StarsAmount amount) {
return base::duplicate(star).append(
Lang::FormatStarsAmountDecimal(amount));
}),
lt_link,
tr::lng_gift_send_stars_balance_link(
) | Ui::Text::ToLink(),
Ui::Text::WithEntities));
struct State {
Settings::BuyStarsHandler buyStars;
rpl::variable<bool> loading;
};
const auto state = balance->lifetime().make_state<State>();
state->loading = state->buyStars.loadingValue();
balance->setClickHandlerFilter([=](const auto &...) {
if (!state->loading.current()) {
state->buyStars.handler(window->uiShow())();
}
return false;
});
}
}, [&](const GiftTypeStars &) { }, [&](const GiftTypeStars &) {
AddDividerText(container, peer->isSelf() AddDividerText(container, peer->isSelf()
? tr::lng_gift_send_anonymous_self() ? tr::lng_gift_send_anonymous_self()
: peer->isBroadcast() : peer->isBroadcast()
? tr::lng_gift_send_anonymous_about_channel() ? tr::lng_gift_send_anonymous_about_channel()
: rpl::conditional( : tr::lng_gift_send_anonymous_about(
state->messageAllowed.value(), lt_user,
tr::lng_gift_send_anonymous_about( rpl::single(peer->shortName()),
lt_user, lt_recipient,
rpl::single(peer->shortName()), rpl::single(peer->shortName())));
lt_recipient,
rpl::single(peer->shortName())),
tr::lng_gift_send_anonymous_about_paid(
lt_user,
rpl::single(peer->shortName()),
lt_recipient,
rpl::single(peer->shortName()))));
}); });
const auto buttonWidth = st::boxWideWidth const auto buttonWidth = st::boxWideWidth
@ -1680,20 +1575,13 @@ void SendGiftBox(
return; return;
} }
state->submitting = true; state->submitting = true;
auto details = state->details.current(); const auto details = state->details.current();
if (!state->messageAllowed.current()) {
details.text = {};
}
const auto weak = MakeWeak(box); const auto weak = MakeWeak(box);
const auto done = [=](Payments::CheckoutResult result) { const auto done = [=](Payments::CheckoutResult result) {
if (result == Payments::CheckoutResult::Paid) { if (result == Payments::CheckoutResult::Paid) {
if (details.byStars
|| v::is<GiftTypeStars>(details.descriptor)) {
window->session().credits().load(true);
}
const auto copy = state->media; const auto copy = state->media;
window->showPeerHistory(peer); window->showPeerHistory(peer);
ShowSentToast(window, details.descriptor, details); ShowSentToast(window, descriptor, details);
} }
if (const auto strong = weak.data()) { if (const auto strong = weak.data()) {
box->closeBox(); box->closeBox();
@ -1926,8 +1814,6 @@ void GiftBox(
box->setCustomCornersFilling(RectPart::FullTop); box->setCustomCornersFilling(RectPart::FullTop);
box->addButton(tr::lng_create_group_back(), [=] { box->closeBox(); }); box->addButton(tr::lng_create_group_back(), [=] { box->closeBox(); });
window->session().credits().load();
FillBg(box); FillBg(box);
const auto &stUser = st::premiumGiftsUserpicButton; const auto &stUser = st::premiumGiftsUserpicButton;
@ -2184,66 +2070,7 @@ void ChooseStarGiftRecipient(
void ShowStarGiftBox( void ShowStarGiftBox(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<PeerData*> peer) { not_null<PeerData*> peer) {
struct Session { controller->show(Box(GiftBox, controller, peer));
PeerData *peer = nullptr;
bool premiumGiftsReady = false;
bool starsGiftsReady = false;
rpl::lifetime lifetime;
};
static auto Map = base::flat_map<not_null<Main::Session*>, Session>();
const auto session = &controller->session();
auto i = Map.find(session);
if (i == end(Map)) {
i = Map.emplace(session).first;
session->lifetime().add([=] { Map.remove(session); });
} else if (i->second.peer == peer) {
return;
}
i->second = Session{ .peer = peer };
const auto weak = base::make_weak(controller);
const auto show = [=] {
Map[session] = Session();
if (const auto strong = weak.get()) {
strong->show(Box(GiftBox, strong, peer));
}
};
base::timer_once(
kGiftsPreloadTimeout
) | rpl::start_with_next(show, i->second.lifetime);
const auto user = peer->asUser();
if (user && !user->isSelf()) {
GiftsPremium(
session,
peer
) | rpl::start_with_next([=](PremiumGiftsDescriptor &&gifts) {
if (!gifts.list.empty()) {
auto &entry = Map[session];
entry.premiumGiftsReady = true;
if (entry.starsGiftsReady) {
show();
}
}
}, i->second.lifetime);
} else {
i->second.premiumGiftsReady = true;
}
GiftsStars(
session,
peer
) | rpl::start_with_next([=](std::vector<GiftTypeStars> &&gifts) {
if (!gifts.empty()) {
auto &entry = Map[session];
entry.starsGiftsReady = true;
if (entry.premiumGiftsReady) {
show();
}
}
}, i->second.lifetime);
} }
void AddUniqueGiftCover( void AddUniqueGiftCover(

View file

@ -771,43 +771,30 @@ void StickerSetBox::updateButtons() {
const auto addPackOwner = [=](const std::shared_ptr<base::unique_qptr<Ui::PopupMenu>> &menu) const auto addPackOwner = [=](const std::shared_ptr<base::unique_qptr<Ui::PopupMenu>> &menu)
{ {
if (type == Data::StickersType::Stickers || type == Data::StickersType::Emoji) { if (type == Data::StickersType::Stickers || type == Data::StickersType::Emoji) {
const auto weak = Ui::MakeWeak(this); const auto pointer = Ui::MakeWeak(this);
const auto session = _session;
const auto innerId = _inner->setId() >> 32;
(*menu)->addAction( (*menu)->addAction(
tr::ayu_MessageDetailsPackOwnerPC(tr::now), tr::ayu_MessageDetailsPackOwnerPC(tr::now),
[weak, session, innerId] [=]
{ {
if (!weak) { if (!pointer) {
return;
}
const auto strong = weak.data();
if (!strong) {
return; return;
} }
searchById( searchById(
innerId, _inner->setId() >> 32,
session, _session,
[session, weak](const QString &username, UserData *user) [=](const QString &username, UserData *user)
{ {
if (!weak) { if (!pointer) {
return;
}
const auto strongInner = weak.data();
if (!strongInner) {
return; return;
} }
if (!user) { if (!user) {
strongInner->showToast(tr::ayu_UserNotFoundMessage(tr::now)); showToast(tr::ayu_UserNotFoundMessage(tr::now));
return; return;
} }
if (const auto window = session->tryResolveWindow()) { if (const auto window = _session->tryResolveWindow()) {
if (const auto mainWidget = window->widget()->sessionController()) { if (const auto mainWidget = window->widget()->sessionController()) {
mainWidget->showPeer(user); mainWidget->showPeer(user);
} }

View file

@ -150,7 +150,10 @@ void TranslateBox(
original->entity()->setAnimationsPausedCallback(animationsPaused); original->entity()->setAnimationsPausedCallback(animationsPaused);
original->entity()->setMarkedText( original->entity()->setMarkedText(
text, text,
Core::TextContext({ .session = &peer->session() })); Core::MarkedTextContext{
.session = &peer->session(),
.customEmojiRepaint = [=] { original->entity()->update(); },
});
original->setMinimalHeight(lineHeight); original->setMinimalHeight(lineHeight);
original->hide(anim::type::instant); original->hide(anim::type::instant);
@ -218,7 +221,10 @@ void TranslateBox(
const auto label = translated->entity(); const auto label = translated->entity();
label->setMarkedText( label->setMarkedText(
text, text,
Core::TextContext({ .session = &peer->session() })); Core::MarkedTextContext{
.session = &peer->session(),
.customEmojiRepaint = [=] { label->update(); },
});
translated->show(anim::type::instant); translated->show(anim::type::instant);
loading->hide(anim::type::instant); loading->hide(anim::type::instant);
}; };

View file

@ -132,12 +132,8 @@ object_ptr<ShareBox> ShareInviteLinkBox(
QGuiApplication::clipboard()->setText(currentLink()); QGuiApplication::clipboard()->setText(currentLink());
show->showToast(tr::lng_group_invite_copied(tr::now)); show->showToast(tr::lng_group_invite_copied(tr::now));
}; };
auto countMessagesCallback = [=](const TextWithTags &comment) {
return 1;
};
auto submitCallback = [=]( auto submitCallback = [=](
std::vector<not_null<Data::Thread*>> &&result, std::vector<not_null<Data::Thread*>> &&result,
Fn<bool()> checkPaid,
TextWithTags &&comment, TextWithTags &&comment,
Api::SendOptions options, Api::SendOptions options,
Data::ForwardOptions) { Data::ForwardOptions) {
@ -154,8 +150,6 @@ object_ptr<ShareBox> ShareInviteLinkBox(
MakeSendErrorBox(error, result.size() > 1)); MakeSendErrorBox(error, result.size() > 1));
} }
return; return;
} else if (!checkPaid()) {
return;
} }
*sending = true; *sending = true;
@ -184,7 +178,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
}; };
auto filterCallback = [](not_null<Data::Thread*> thread) { auto filterCallback = [](not_null<Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) { if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreMoneyRestrictions()) { if (user->canSendIgnoreRequirePremium()) {
return true; return true;
} }
} }
@ -195,7 +189,6 @@ object_ptr<ShareBox> ShareInviteLinkBox(
auto result = Box<ShareBox>(ShareBox::Descriptor{ auto result = Box<ShareBox>(ShareBox::Descriptor{
.session = &peer->session(), .session = &peer->session(),
.copyCallback = std::move(copyCallback), .copyCallback = std::move(copyCallback),
.countMessagesCallback = std::move(countMessagesCallback),
.submitCallback = std::move(submitCallback), .submitCallback = std::move(submitCallback),
.filterCallback = std::move(filterCallback), .filterCallback = std::move(filterCallback),
.bottomWidget = std::move(bottom), .bottomWidget = std::move(bottom),
@ -206,7 +199,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
tr::lng_group_call_copy_speaker_link(), tr::lng_group_call_copy_speaker_link(),
tr::lng_group_call_copy_listener_link()), tr::lng_group_call_copy_listener_link()),
.st = st.shareBox ? *st.shareBox : ShareBoxStyleOverrides(), .st = st.shareBox ? *st.shareBox : ShareBoxStyleOverrides(),
.moneyRestrictionError = ShareMessageMoneyRestrictionError(), .premiumRequiredError = SharePremiumRequiredError(),
}); });
*box = result.data(); *box = result.data();
return result; return result;

View file

@ -149,7 +149,6 @@ EmojiButton {
SendButton { SendButton {
inner: IconButton; inner: IconButton;
stars: RoundButton;
record: icon; record: icon;
recordOver: icon; recordOver: icon;
round: icon; round: icon;
@ -856,10 +855,6 @@ historyComposeButton: FlatButton {
color: historyComposeButtonBgRipple; color: historyComposeButtonBgRipple;
} }
} }
historyComposeButtonText: FlatLabel(defaultFlatLabel) {
style: semiboldTextStyle;
textFg: windowActiveTextFg;
}
historyGiftToChannel: IconButton(defaultIconButton) { historyGiftToChannel: IconButton(defaultIconButton) {
width: 46px; width: 46px;
height: 46px; height: 46px;
@ -918,10 +913,6 @@ historyBusinessBotSettings: IconButton(defaultIconButton) {
height: 58px; height: 58px;
width: 48px; width: 48px;
} }
paysStatusLabel: FlatLabel(historyBusinessBotStatus) {
align: align(top);
minWidth: 240px;
}
historyReplyCancelIcon: icon {{ "box_button_close", historyReplyCancelFg }}; historyReplyCancelIcon: icon {{ "box_button_close", historyReplyCancelFg }};
historyReplyCancelIconOver: icon {{ "box_button_close", historyReplyCancelFgOver }}; historyReplyCancelIconOver: icon {{ "box_button_close", historyReplyCancelFgOver }};
@ -1298,12 +1289,6 @@ historySend: SendButton {
icon: historySendIcon; icon: historySendIcon;
iconOver: historySendIconOver; iconOver: historySendIconOver;
} }
stars: RoundButton(defaultActiveButton) {
height: 28px;
padding: margins(0px, 0px, 6px, 0px);
textTop: 5px;
width: -8px;
}
record: historyRecordVoice; record: historyRecordVoice;
recordOver: historyRecordVoiceOver; recordOver: historyRecordVoiceOver;
round: historyRecordRound; round: historyRecordRound;

View file

@ -1747,8 +1747,7 @@ void InitFieldAutocomplete(
&& peer->isUser() && peer->isUser()
&& !peer->asUser()->isBot() && !peer->asUser()->isBot()
&& (!shortcutMessages && (!shortcutMessages
|| shortcutMessages->shortcuts().list.empty() || shortcutMessages->shortcuts().list.empty())) {
|| peer->starsPerMessageChecked() != 0)) {
parsed = {}; parsed = {};
} }
raw->showFiltered(peer, parsed.query, parsed.fromStart); raw->showFiltered(peer, parsed.query, parsed.fromStart);

View file

@ -695,15 +695,14 @@ GifsListWidget::LayoutItem *GifsListWidget::layoutPrepareSavedGif(
} }
GifsListWidget::LayoutItem *GifsListWidget::layoutPrepareInlineResult( GifsListWidget::LayoutItem *GifsListWidget::layoutPrepareInlineResult(
std::shared_ptr<InlineResult> result) { not_null<InlineResult*> result) {
const auto raw = result.get(); auto it = _inlineLayouts.find(result);
auto it = _inlineLayouts.find(raw);
if (it == _inlineLayouts.cend()) { if (it == _inlineLayouts.cend()) {
if (auto layout = LayoutItem::createLayout( if (auto layout = LayoutItem::createLayout(
this, this,
std::move(result), result,
_inlineWithThumb)) { _inlineWithThumb)) {
it = _inlineLayouts.emplace(raw, std::move(layout)).first; it = _inlineLayouts.emplace(result, std::move(layout)).first;
it->second->initDimensions(); it->second->initDimensions();
} else { } else {
return nullptr; return nullptr;
@ -776,8 +775,8 @@ int GifsListWidget::refreshInlineRows(const InlineCacheEntry *entry, bool result
from, from,
count count
) | ranges::views::transform([&]( ) | ranges::views::transform([&](
const std::shared_ptr<InlineBots::Result> &r) { const std::unique_ptr<InlineBots::Result> &r) {
return layoutPrepareInlineResult(r); return layoutPrepareInlineResult(r.get());
}) | ranges::views::filter([](const LayoutItem *item) { }) | ranges::views::filter([](const LayoutItem *item) {
return item != nullptr; return item != nullptr;
}) | ranges::to<std::vector<not_null<LayoutItem*>>>; }) | ranges::to<std::vector<not_null<LayoutItem*>>>;
@ -800,7 +799,7 @@ int GifsListWidget::validateExistingInlineRows(const InlineResults &results) {
const auto until = _mosaic.validateExistingRows([&]( const auto until = _mosaic.validateExistingRows([&](
not_null<const LayoutItem*> item, not_null<const LayoutItem*> item,
int untilIndex) { int untilIndex) {
return item->getResult().get() != results[untilIndex].get(); return item->getResult() != results[untilIndex].get();
}, results.size()); }, results.size());
if (_mosaic.empty()) { if (_mosaic.empty()) {

View file

@ -131,7 +131,7 @@ private:
}; };
using InlineResult = InlineBots::Result; using InlineResult = InlineBots::Result;
using InlineResults = std::vector<std::shared_ptr<InlineResult>>; using InlineResults = std::vector<std::unique_ptr<InlineResult>>;
using LayoutItem = InlineBots::Layout::ItemBase; using LayoutItem = InlineBots::Layout::ItemBase;
struct InlineCacheEntry { struct InlineCacheEntry {
@ -162,8 +162,7 @@ private:
void clearInlineRows(bool resultsDeleted); void clearInlineRows(bool resultsDeleted);
LayoutItem *layoutPrepareSavedGif(not_null<DocumentData*> document); LayoutItem *layoutPrepareSavedGif(not_null<DocumentData*> document);
LayoutItem *layoutPrepareInlineResult( LayoutItem *layoutPrepareInlineResult(not_null<InlineResult*> result);
std::shared_ptr<InlineResult> result);
void deleteUnusedGifLayouts(); void deleteUnusedGifLayouts();

View file

@ -42,7 +42,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
#include "styles/style_chat_helpers.h" #include "styles/style_chat_helpers.h"
#include "styles/style_credits.h"
#include "styles/style_settings.h" #include "styles/style_settings.h"
#include "base/qt/qt_common_adapters.h" #include "base/qt/qt_common_adapters.h"
@ -433,9 +432,12 @@ void InitMessageFieldHandlers(MessageFieldHandlersArgs &&args) {
const auto session = args.session; const auto session = args.session;
field->setTagMimeProcessor( field->setTagMimeProcessor(
FieldTagMimeProcessor(session, args.allowPremiumEmoji)); FieldTagMimeProcessor(session, args.allowPremiumEmoji));
field->setCustomTextContext(Core::TextContext({ field->setCustomTextContext([=](Fn<void()> repaint) {
.session = session return std::any(Core::MarkedTextContext{
}), [paused] { .session = session,
.customEmojiRepaint = std::move(repaint),
});
}, [paused] {
return On(PowerSaving::kEmojiChat) || paused(); return On(PowerSaving::kEmojiChat) || paused();
}, [paused] { }, [paused] {
return On(PowerSaving::kChatSpoiler) || paused(); return On(PowerSaving::kChatSpoiler) || paused();
@ -1278,26 +1280,3 @@ void SelectTextInFieldWithMargins(
textCursor.setPosition(selection.to, QTextCursor::KeepAnchor); textCursor.setPosition(selection.to, QTextCursor::KeepAnchor);
field->setTextCursor(textCursor); field->setTextCursor(textCursor);
} }
TextWithEntities PaidSendButtonText(tr::now_t, int stars) {
return Ui::Text::IconEmoji(&st::starIconEmoji).append(
Lang::FormatCountToShort(stars).string);
}
rpl::producer<TextWithEntities> PaidSendButtonText(
rpl::producer<int> stars,
rpl::producer<QString> fallback) {
if (fallback) {
return rpl::combine(
std::move(fallback),
std::move(stars)
) | rpl::map([=](QString zero, int count) {
return count
? PaidSendButtonText(tr::now, count)
: TextWithEntities{ zero };
});
}
return std::move(stars) | rpl::map([=](int count) {
return PaidSendButtonText(tr::now, count);
});
}

View file

@ -19,10 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
namespace tr {
struct now_t;
} // namespace tr
namespace Main { namespace Main {
class Session; class Session;
class SessionShow; class SessionShow;
@ -173,8 +169,3 @@ private:
void SelectTextInFieldWithMargins( void SelectTextInFieldWithMargins(
not_null<Ui::InputField*> field, not_null<Ui::InputField*> field,
const TextSelection &selection); const TextSelection &selection);
[[nodiscard]] TextWithEntities PaidSendButtonText(tr::now_t, int stars);
[[nodiscard]] rpl::producer<TextWithEntities> PaidSendButtonText(
rpl::producer<int> stars,
rpl::producer<QString> fallback = nullptr);

View file

@ -37,7 +37,7 @@ std::map<int, const char*> BetaLogs() {
"- Nice looking code blocks with syntax highlight.\n" "- Nice looking code blocks with syntax highlight.\n"
"- Copy full code block by click on its header.\n" "- Copy full code block by click on its header.\n"
"- Send a highlighted code block using ```language syntax.\n" "- Send a highlighted code block using ```language syntax.\n"
} }
}; };

View file

@ -205,13 +205,13 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const {
}); });
}; };
if (_bot->isVerified() if (_bot->isVerified()
|| _bot->session().local().isPeerTrustedOpenGame(_bot->id)) { || _bot->session().local().isBotTrustedOpenGame(_bot->id)) {
openGame(); openGame();
} else { } else {
if (const auto controller = my.sessionWindow.get()) { if (const auto controller = my.sessionWindow.get()) {
const auto callback = [=, bot = _bot](Fn<void()> close) { const auto callback = [=, bot = _bot](Fn<void()> close) {
close(); close();
bot->session().local().markPeerTrustedOpenGame(bot->id); bot->session().local().markBotTrustedOpenGame(bot->id);
openGame(); openGame();
}; };
controller->show(Ui::MakeConfirmBox({ controller->show(Ui::MakeConfirmBox({

View file

@ -25,7 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/edit_birthday_box.h" #include "ui/boxes/edit_birthday_box.h"
#include "ui/integration.h" #include "ui/integration.h"
#include "payments/payments_non_panel_process.h" #include "payments/payments_non_panel_process.h"
#include "boxes/peers/edit_peer_info_box.h"
#include "boxes/share_box.h" #include "boxes/share_box.h"
#include "boxes/connection_box.h" #include "boxes/connection_box.h"
#include "boxes/gift_premium_box.h" #include "boxes/gift_premium_box.h"
@ -69,8 +68,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_domain.h" #include "main/main_domain.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "main/main_session_settings.h" #include "main/main_session_settings.h"
#include "info/info_controller.h"
#include "info/info_memento.h"
#include "inline_bots/bot_attach_web_view.h" #include "inline_bots/bot_attach_web_view.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
@ -1021,45 +1018,6 @@ bool CopyUsername(
return true; return true;
} }
bool EditPaidMessagesFee(
Window::SessionController *controller,
const Match &match,
const QVariant &context) {
if (!controller) {
return false;
}
const auto peerId = PeerId(match->captured(1).toULongLong());
if (const auto id = peerToChannel(peerId)) {
const auto channel = controller->session().data().channelLoaded(id);
if (channel && channel->canEditPermissions()) {
ShowEditChatPermissions(controller, channel);
}
} else {
controller->show(Box(EditMessagesPrivacyBox, controller));
}
return true;
}
bool ShowCommonGroups(
Window::SessionController *controller,
const Match &match,
const QVariant &context) {
if (!controller) {
return false;
}
const auto peerId = PeerId(match->captured(1).toULongLong());
if (const auto id = peerToUser(peerId)) {
const auto user = controller->session().data().userLoaded(id);
if (user) {
controller->showSection(
std::make_shared<Info::Memento>(
user,
Info::Section::Type::CommonGroups));
}
}
return true;
}
bool ShowStarsExamples( bool ShowStarsExamples(
Window::SessionController *controller, Window::SessionController *controller,
const Match &match, const Match &match,
@ -1571,14 +1529,6 @@ const std::vector<LocalUrlHandler> &InternalUrlHandlers() {
u"^username_regular/([a-zA-Z0-9\\-\\_\\.]+)@([0-9]+)$"_q, u"^username_regular/([a-zA-Z0-9\\-\\_\\.]+)@([0-9]+)$"_q,
CopyUsername, CopyUsername,
}, },
{
u"^edit_paid_messages_fee/([0-9]+)$"_q,
EditPaidMessagesFee,
},
{
u"^common_groups/([0-9]+)$"_q,
ShowCommonGroups,
},
{ {
u"^stars_examples$"_q, u"^stars_examples$"_q,
ShowStarsExamples, ShowStarsExamples,

View file

@ -127,30 +127,16 @@ ResolvePhoneAction::ResolvePhoneAction(
return; return;
} }
const auto weak = Ui::MakeWeak(this); searchById(possibleId,
const auto session = &controller->session(); &controller->session(),
[=](const QString &username, UserData *user)
{
if (user) {
_peer = user;
}
searchById( _loaded.force_assign(true);
possibleId, });
session,
[session, weak, possibleId](const QString &username, UserData *user)
{
if (!weak) {
return;
}
const auto strong = weak.data();
if (!strong) {
return;
}
if (user) {
strong->_peer = user;
}
strong->_loaded.force_assign(true);
}
);
} }
}).send(); }).send();
} }

View file

@ -76,14 +76,6 @@ const auto CommandByName = base::flat_map<QString, Command>{
{ u"first_chat"_q , Command::ChatFirst }, { u"first_chat"_q , Command::ChatFirst },
{ u"last_chat"_q , Command::ChatLast }, { u"last_chat"_q , Command::ChatLast },
{ u"self_chat"_q , Command::ChatSelf }, { u"self_chat"_q , Command::ChatSelf },
{ u"pinned_chat1"_q , Command::ChatPinned1 },
{ u"pinned_chat2"_q , Command::ChatPinned2 },
{ u"pinned_chat3"_q , Command::ChatPinned3 },
{ u"pinned_chat4"_q , Command::ChatPinned4 },
{ u"pinned_chat5"_q , Command::ChatPinned5 },
{ u"pinned_chat6"_q , Command::ChatPinned6 },
{ u"pinned_chat7"_q , Command::ChatPinned7 },
{ u"pinned_chat8"_q , Command::ChatPinned8 },
{ u"previous_folder"_q , Command::FolderPrevious }, { u"previous_folder"_q , Command::FolderPrevious },
{ u"next_folder"_q , Command::FolderNext }, { u"next_folder"_q , Command::FolderNext },
@ -176,7 +168,6 @@ private:
void set(const QKeySequence &result, Command command, bool replace); void set(const QKeySequence &result, Command command, bool replace);
void remove(const QString &keys); void remove(const QString &keys);
void remove(const QKeySequence &keys); void remove(const QKeySequence &keys);
void remove(const QKeySequence &keys, Command command);
void unregister(base::unique_qptr<QAction> shortcut); void unregister(base::unique_qptr<QAction> shortcut);
void pruneListened(); void pruneListened();
@ -302,7 +293,7 @@ void Manager::change(
Command command, Command command,
std::optional<Command> restore) { std::optional<Command> restore) {
if (!was.isEmpty()) { if (!was.isEmpty()) {
remove(was, command); remove(was);
} }
if (!now.isEmpty()) { if (!now.isEmpty()) {
set(now, command, true); set(now, command, true);
@ -406,7 +397,6 @@ bool Manager::readCustomFile() {
const auto entry = (*i).toObject(); const auto entry = (*i).toObject();
const auto keys = entry.constFind(u"keys"_q); const auto keys = entry.constFind(u"keys"_q);
const auto command = entry.constFind(u"command"_q); const auto command = entry.constFind(u"command"_q);
const auto removed = entry.constFind(u"removed"_q);
if (keys == entry.constEnd() if (keys == entry.constEnd()
|| command == entry.constEnd() || command == entry.constEnd()
|| !(*keys).isString() || !(*keys).isString()
@ -420,11 +410,7 @@ bool Manager::readCustomFile() {
const auto name = (*command).toString(); const auto name = (*command).toString();
const auto i = CommandByName.find(name); const auto i = CommandByName.find(name);
if (i != end(CommandByName)) { if (i != end(CommandByName)) {
if (removed != entry.constEnd() && removed->toBool()) { set((*keys).toString(), i->second, true);
remove((*keys).toString(), i->second);
} else {
set((*keys).toString(), i->second, true);
}
} else { } else {
LOG(("Shortcut Warning: " LOG(("Shortcut Warning: "
"could not find shortcut command handler '%1'" "could not find shortcut command handler '%1'"
@ -579,36 +565,12 @@ void Manager::writeCustomFile() {
} }
} }
} }
const auto has = [&](not_null<QObject*> shortcut, Command command) { for (const auto &[sequence, command] : _defaults) {
for (auto i = _commandByObject.findFirst(shortcut) if (!_shortcuts.contains(sequence)) {
; i != end(_commandByObject) && i->first == shortcut
; ++i) {
if (i->second == command) {
return true;
}
}
return false;
};
for (const auto &[sequence, commands] : _defaults) {
const auto i = _shortcuts.find(sequence);
if (i == end(_shortcuts)) {
QJsonObject entry; QJsonObject entry;
entry.insert(u"keys"_q, sequence.toString().toLower()); entry.insert(u"keys"_q, sequence.toString().toLower());
entry.insert(u"command"_q, QJsonValue()); entry.insert(u"command"_q, QJsonValue());
shortcuts.append(entry); shortcuts.append(entry);
continue;
}
for (const auto command : commands) {
if (!has(i->second.get(), command)) {
const auto j = CommandNames().find(command);
if (j != CommandNames().end()) {
QJsonObject entry;
entry.insert(u"keys"_q, sequence.toString().toLower());
entry.insert(u"command"_q, j->second);
entry.insert(u"removed"_q, true);
shortcuts.append(entry);
}
}
} }
} }
@ -707,17 +669,6 @@ void Manager::remove(const QKeySequence &keys) {
} }
} }
void Manager::remove(const QKeySequence &keys, Command command) {
const auto i = _shortcuts.find(keys);
if (i != end(_shortcuts)) {
_commandByObject.remove(i->second.get(), command);
if (!_commandByObject.contains(i->second.get())) {
unregister(std::move(i->second));
_shortcuts.erase(i);
}
}
}
void Manager::unregister(base::unique_qptr<QAction> shortcut) { void Manager::unregister(base::unique_qptr<QAction> shortcut) {
if (shortcut) { if (shortcut) {
_commandByObject.removeAll(shortcut.get()); _commandByObject.removeAll(shortcut.get());

View file

@ -60,11 +60,6 @@ public:
normalize(); normalize();
return *this; return *this;
} }
inline StarsAmount operator-() const {
auto result = *this;
result *= -1;
return result;
}
friend inline auto operator<=>(StarsAmount, StarsAmount) = default; friend inline auto operator<=>(StarsAmount, StarsAmount) = default;
friend inline bool operator==(StarsAmount, StarsAmount) = default; friend inline bool operator==(StarsAmount, StarsAmount) = default;
@ -102,7 +97,3 @@ private:
[[nodiscard]] inline StarsAmount operator*(StarsAmount a, int64 b) { [[nodiscard]] inline StarsAmount operator*(StarsAmount a, int64 b) {
return a *= b; return a *= b;
} }
[[nodiscard]] inline StarsAmount operator*(int64 a, StarsAmount b) {
return b *= a;
}

View file

@ -17,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "iv/iv_instance.h" #include "iv/iv_instance.h"
#include "ui/text/text_custom_emoji.h" #include "ui/text/text_custom_emoji.h"
#include "ui/text/text_utilities.h"
#include "ui/basic_click_handlers.h" #include "ui/basic_click_handlers.h"
#include "ui/emoji_config.h" #include "ui/emoji_config.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
@ -113,40 +112,6 @@ const auto kBadPrefix = u"http://"_q;
} // namespace } // namespace
Ui::Text::MarkedContext TextContext(TextContextArgs &&args) {
using Context = Ui::Text::MarkedContext;
using Factory = Ui::Text::CustomEmojiFactory;
const auto session = args.session;
auto simple = [session](QStringView data, const Context &context) {
return session->data().customEmojiManager().create(
data,
context.repaint);
};
auto factory = !args.customEmojiLoopLimit
? Factory(simple)
: (args.customEmojiLoopLimit > 0)
? Factory([simple, loop = args.customEmojiLoopLimit](
QStringView data,
const Context &context) {
return std::make_unique<Ui::Text::LimitedLoopsEmoji>(
simple(data, context),
loop);
})
: Factory([simple](
QStringView data,
const Context &context) {
return std::make_unique<Ui::Text::FirstFrameEmoji>(
simple(data, context));
});
args.details.session = session;
return {
.repaint = std::move(args.repaint),
.customEmojiFactory = std::move(factory),
.other = std::move(args.details),
};
}
void UiIntegration::postponeCall(FnMut<void()> &&callable) { void UiIntegration::postponeCall(FnMut<void()> &&callable) {
Sandbox::Instance().postponeCall(std::move(callable)); Sandbox::Instance().postponeCall(std::move(callable));
} }
@ -187,8 +152,8 @@ bool UiIntegration::screenIsLocked() {
std::shared_ptr<ClickHandler> UiIntegration::createLinkHandler( std::shared_ptr<ClickHandler> UiIntegration::createLinkHandler(
const EntityLinkData &data, const EntityLinkData &data,
const Ui::Text::MarkedContext &context) { const std::any &context) {
const auto my = std::any_cast<Core::TextContextDetails>(&context.other); const auto my = std::any_cast<MarkedTextContext>(&context);
switch (data.type) { switch (data.type) {
case EntityType::Url: case EntityType::Url:
return (!data.data.isEmpty() return (!data.data.isEmpty()
@ -205,7 +170,7 @@ std::shared_ptr<ClickHandler> UiIntegration::createLinkHandler(
return std::make_shared<BotCommandClickHandler>(data.data); return std::make_shared<BotCommandClickHandler>(data.data);
case EntityType::Hashtag: case EntityType::Hashtag:
using HashtagMentionType = TextContextDetails::HashtagMentionType; using HashtagMentionType = MarkedTextContext::HashtagMentionType;
if (my && my->type == HashtagMentionType::Twitter) { if (my && my->type == HashtagMentionType::Twitter) {
return std::make_shared<UrlClickHandler>( return std::make_shared<UrlClickHandler>(
(u"https://twitter.com/hashtag/"_q (u"https://twitter.com/hashtag/"_q
@ -225,7 +190,7 @@ std::shared_ptr<ClickHandler> UiIntegration::createLinkHandler(
return std::make_shared<CashtagClickHandler>(data.data); return std::make_shared<CashtagClickHandler>(data.data);
case EntityType::Mention: case EntityType::Mention:
using HashtagMentionType = TextContextDetails::HashtagMentionType; using HashtagMentionType = MarkedTextContext::HashtagMentionType;
if (my && my->type == HashtagMentionType::Twitter) { if (my && my->type == HashtagMentionType::Twitter) {
return std::make_shared<UrlClickHandler>( return std::make_shared<UrlClickHandler>(
u"https://twitter.com/"_q + data.data.mid(1), u"https://twitter.com/"_q + data.data.mid(1),
@ -257,9 +222,7 @@ std::shared_ptr<ClickHandler> UiIntegration::createLinkHandler(
case EntityType::Pre: case EntityType::Pre:
return std::make_shared<MonospaceClickHandler>(data.text, data.type); return std::make_shared<MonospaceClickHandler>(data.text, data.type);
case EntityType::Phone: case EntityType::Phone:
return my->session return std::make_shared<PhoneClickHandler>(my->session, data.text);
? std::make_shared<PhoneClickHandler>(my->session, data.text)
: nullptr;
} }
return Integration::createLinkHandler(data, context); return Integration::createLinkHandler(data, context);
} }
@ -317,6 +280,36 @@ bool UiIntegration::copyPreOnClick(const QVariant &context) {
return true; return true;
} }
std::unique_ptr<Ui::Text::CustomEmoji> UiIntegration::createCustomEmoji(
QStringView data,
const std::any &context) {
const auto my = std::any_cast<MarkedTextContext>(&context);
if (!my || !my->session) {
return nullptr;
}
auto result = my->session->data().customEmojiManager().create(
data,
my->customEmojiRepaint);
if (my->customEmojiLoopLimit > 0) {
return std::make_unique<Ui::Text::LimitedLoopsEmoji>(
std::move(result),
my->customEmojiLoopLimit);
} else if (my->customEmojiLoopLimit) {
return std::make_unique<Ui::Text::FirstFrameEmoji>(
std::move(result));
}
return result;
}
Fn<void()> UiIntegration::createSpoilerRepaint(const std::any &context) {
const auto my = std::any_cast<MarkedTextContext>(&context);
if (my) {
return my->customEmojiRepaint;
}
const auto common = std::any_cast<CommonTextContext>(&context);
return common ? common->repaint : nullptr;
}
rpl::producer<> UiIntegration::forcePopupMenuHideRequests() { rpl::producer<> UiIntegration::forcePopupMenuHideRequests() {
return Core::App().passcodeLockChanges() | rpl::to_empty; return Core::App().passcodeLockChanges() | rpl::to_empty;
} }

View file

@ -19,7 +19,7 @@ class ElementDelegate;
namespace Core { namespace Core {
struct TextContextDetails { struct MarkedTextContext {
enum class HashtagMentionType : uchar { enum class HashtagMentionType : uchar {
Telegram, Telegram,
Twitter, Twitter,
@ -28,15 +28,9 @@ struct TextContextDetails {
Main::Session *session = nullptr; Main::Session *session = nullptr;
HashtagMentionType type = HashtagMentionType::Telegram; HashtagMentionType type = HashtagMentionType::Telegram;
}; Fn<void()> customEmojiRepaint;
struct TextContextArgs {
not_null<Main::Session*> session;
TextContextDetails details;
Fn<void()> repaint;
int customEmojiLoopLimit = 0; int customEmojiLoopLimit = 0;
}; };
[[nodiscard]] Ui::Text::MarkedContext TextContext(TextContextArgs &&args);
class UiIntegration final : public Ui::Integration { class UiIntegration final : public Ui::Integration {
public: public:
@ -55,7 +49,7 @@ public:
std::shared_ptr<ClickHandler> createLinkHandler( std::shared_ptr<ClickHandler> createLinkHandler(
const EntityLinkData &data, const EntityLinkData &data,
const Ui::Text::MarkedContext &context) override; const std::any &context) override;
bool handleUrlClick( bool handleUrlClick(
const QString &url, const QString &url,
const QVariant &context) override; const QVariant &context) override;
@ -63,6 +57,10 @@ public:
rpl::producer<> forcePopupMenuHideRequests() override; rpl::producer<> forcePopupMenuHideRequests() override;
const Ui::Emoji::One *defaultEmojiVariant( const Ui::Emoji::One *defaultEmojiVariant(
const Ui::Emoji::One *emoji) override; const Ui::Emoji::One *emoji) override;
std::unique_ptr<Ui::Text::CustomEmoji> createCustomEmoji(
QStringView data,
const std::any &context) override;
Fn<void()> createSpoilerRepaint(const std::any &context) override;
QString phraseContextCopyText() override; QString phraseContextCopyText() override;
QString phraseContextCopyEmail() override; QString phraseContextCopyEmail() override;

View file

@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
constexpr auto AppNameOld = "AyuGram for Windows"_cs; constexpr auto AppNameOld = "AyuGram for Windows"_cs;
constexpr auto AppName = "AyuGram Desktop"_cs; constexpr auto AppName = "AyuGram Desktop"_cs;
constexpr auto AppFile = "AyuGram"_cs; constexpr auto AppFile = "AyuGram"_cs;
constexpr auto AppVersion = 5012003; constexpr auto AppVersion = 5011001;
constexpr auto AppVersionStr = "5.12.3"; constexpr auto AppVersionStr = "5.11.1";
constexpr auto AppBetaVersion = false; constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View file

@ -322,9 +322,6 @@ QString CountriesInstance::flagEmojiByISO2(const QString &iso) const {
|| iso.back() < 'A' || iso.back() < 'A'
|| iso.back() > 'Z') { || iso.back() > 'Z') {
return QString(); return QString();
} else if (iso == u"FT"_q) {
return QString::fromUtf8(
"\xF0\x9F\x8F\xB4\xE2\x80\x8D\xE2\x98\xA0\xEF\xB8\x8F");
} }
auto result = QString(4, QChar(0xD83C)); auto result = QString(4, QChar(0xD83C));
result[1] = QChar(iso.front().unicode() - 'A' + 0xDDE6); result[1] = QChar(iso.front().unicode() - 'A' + 0xDDE6);

View file

@ -91,8 +91,7 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
MTP_int(shortcutId), MTP_int(shortcutId),
MTP_long(data.veffect().value_or_empty()), MTP_long(data.veffect().value_or_empty()),
(data.vfactcheck() ? *data.vfactcheck() : MTPFactCheck()), (data.vfactcheck() ? *data.vfactcheck() : MTPFactCheck()),
MTP_int(data.vreport_delivery_until_date().value_or_empty()), MTP_int(data.vreport_delivery_until_date().value_or_empty()));
MTP_long(data.vpaid_message_stars().value_or_empty()));
}); });
} }

View file

@ -37,7 +37,10 @@ void Credits::apply(const MTPDupdateStarsBalance &data) {
rpl::producer<float64> Credits::rateValue( rpl::producer<float64> Credits::rateValue(
not_null<PeerData*> ownedBotOrChannel) { not_null<PeerData*> ownedBotOrChannel) {
return rpl::single(_session->appConfig().starsWithdrawRate()); return rpl::single(
_session->appConfig().get<float64>(
u"stars_usd_withdraw_rate_x1000"_q,
1200) / 1000.);
} }
void Credits::load(bool force) { void Credits::load(bool force) {

View file

@ -95,8 +95,7 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
MTPint(), // quick_reply_shortcut_id MTPint(), // quick_reply_shortcut_id
MTP_long(data.veffect().value_or_empty()), // effect MTP_long(data.veffect().value_or_empty()), // effect
data.vfactcheck() ? *data.vfactcheck() : MTPFactCheck(), data.vfactcheck() ? *data.vfactcheck() : MTPFactCheck(),
MTP_int(data.vreport_delivery_until_date().value_or_empty()), MTP_int(data.vreport_delivery_until_date().value_or_empty()));
MTP_long(data.vpaid_message_stars().value_or_empty()));
}); });
} }
@ -270,8 +269,7 @@ void ScheduledMessages::sendNowSimpleMessage(
MTPint(), // quick_reply_shortcut_id MTPint(), // quick_reply_shortcut_id
MTP_long(local->effectId()), // effect MTP_long(local->effectId()), // effect
MTPFactCheck(), MTPFactCheck(),
MTPint(), // report_delivery_until_date MTPint()), // report_delivery_until_date
MTPlong()), // paid_message_stars
localFlags, localFlags,
NewMessageType::Unread); NewMessageType::Unread);

View file

@ -75,48 +75,46 @@ struct PeerUpdate {
BackgroundEmoji = (1ULL << 15), BackgroundEmoji = (1ULL << 15),
StoriesState = (1ULL << 16), StoriesState = (1ULL << 16),
VerifyInfo = (1ULL << 17), VerifyInfo = (1ULL << 17),
StarsPerMessage = (1ULL << 18),
// For users // For users
CanShareContact = (1ULL << 19), CanShareContact = (1ULL << 18),
IsContact = (1ULL << 20), IsContact = (1ULL << 19),
PhoneNumber = (1ULL << 21), PhoneNumber = (1ULL << 20),
OnlineStatus = (1ULL << 22), OnlineStatus = (1ULL << 21),
BotCommands = (1ULL << 23), BotCommands = (1ULL << 22),
BotCanBeInvited = (1ULL << 24), BotCanBeInvited = (1ULL << 23),
BotStartToken = (1ULL << 25), BotStartToken = (1ULL << 24),
CommonChats = (1ULL << 26), CommonChats = (1ULL << 25),
PeerGifts = (1ULL << 27), PeerGifts = (1ULL << 26),
HasCalls = (1ULL << 28), HasCalls = (1ULL << 27),
SupportInfo = (1ULL << 29), SupportInfo = (1ULL << 28),
IsBot = (1ULL << 30), IsBot = (1ULL << 29),
EmojiStatus = (1ULL << 31), EmojiStatus = (1ULL << 30),
BusinessDetails = (1ULL << 32), BusinessDetails = (1ULL << 31),
Birthday = (1ULL << 33), Birthday = (1ULL << 32),
PersonalChannel = (1ULL << 34), PersonalChannel = (1ULL << 33),
StarRefProgram = (1ULL << 35), StarRefProgram = (1ULL << 34),
PaysPerMessage = (1ULL << 36),
// For chats and channels // For chats and channels
InviteLinks = (1ULL << 37), InviteLinks = (1ULL << 35),
Members = (1ULL << 38), Members = (1ULL << 36),
Admins = (1ULL << 39), Admins = (1ULL << 37),
BannedUsers = (1ULL << 40), BannedUsers = (1ULL << 38),
Rights = (1ULL << 41), Rights = (1ULL << 39),
PendingRequests = (1ULL << 42), PendingRequests = (1ULL << 40),
Reactions = (1ULL << 43), Reactions = (1ULL << 41),
// For channels // For channels
ChannelAmIn = (1ULL << 44), ChannelAmIn = (1ULL << 42),
StickersSet = (1ULL << 45), StickersSet = (1ULL << 43),
EmojiSet = (1ULL << 46), EmojiSet = (1ULL << 44),
ChannelLinkedChat = (1ULL << 47), ChannelLinkedChat = (1ULL << 45),
ChannelLocation = (1ULL << 48), ChannelLocation = (1ULL << 46),
Slowmode = (1ULL << 49), Slowmode = (1ULL << 47),
GroupCall = (1ULL << 50), GroupCall = (1ULL << 48),
// For iteration // For iteration
LastUsedBit = (1ULL << 50), LastUsedBit = (1ULL << 48),
}; };
using Flags = base::flags<Flag>; using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; } friend inline constexpr auto is_flag_type(Flag) { return true; }

View file

@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h" #include "data/data_channel.h"
#include "api/api_global_privacy.h" #include "api/api_global_privacy.h"
#include "data/components/credits.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_channel_admins.h" #include "data/data_channel_admins.h"
#include "data/data_user.h" #include "data/data_user.h"
@ -31,7 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_chat_invite.h" #include "api/api_chat_invite.h"
#include "api/api_invite_links.h" #include "api/api_invite_links.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "storage/storage_account.h"
#include "ui/unread_badge.h" #include "ui/unread_badge.h"
#include "window/notifications_manager.h" #include "window/notifications_manager.h"
@ -861,21 +859,6 @@ void ChannelData::growSlowmodeLastMessage(TimeId when) {
session().changes().peerUpdated(this, UpdateFlag::Slowmode); session().changes().peerUpdated(this, UpdateFlag::Slowmode);
} }
int ChannelData::starsPerMessage() const {
if (const auto info = mgInfo.get()) {
return info->_starsPerMessage;
}
return 0;
}
void ChannelData::setStarsPerMessage(int stars) {
if (mgInfo && starsPerMessage() != stars) {
mgInfo->_starsPerMessage = stars;
session().changes().peerUpdated(this, UpdateFlag::StarsPerMessage);
}
checkTrustedPayForMessage();
}
int ChannelData::peerGiftsCount() const { int ChannelData::peerGiftsCount() const {
return _peerGiftsCount; return _peerGiftsCount;
} }
@ -1167,8 +1150,7 @@ void ApplyChannelUpdate(
| Flag::CanViewRevenue | Flag::CanViewRevenue
| Flag::PaidMediaAllowed | Flag::PaidMediaAllowed
| Flag::CanViewCreditsRevenue | Flag::CanViewCreditsRevenue
| Flag::StargiftsAvailable | Flag::StargiftsAvailable;
| Flag::PaidMessagesAvailable;
channel->setFlags((channel->flags() & ~mask) channel->setFlags((channel->flags() & ~mask)
| (update.is_can_set_username() ? Flag::CanSetUsername : Flag()) | (update.is_can_set_username() ? Flag::CanSetUsername : Flag())
| (update.is_can_view_participants() | (update.is_can_view_participants()
@ -1192,9 +1174,6 @@ void ApplyChannelUpdate(
: Flag()) : Flag())
| (update.is_stargifts_available() | (update.is_stargifts_available()
? Flag::StargiftsAvailable ? Flag::StargiftsAvailable
: Flag())
| (update.is_paid_messages_available()
? Flag::PaidMessagesAvailable
: Flag())); : Flag()));
channel->setUserpicPhoto(update.vchat_photo()); channel->setUserpicPhoto(update.vchat_photo());
if (const auto migratedFrom = update.vmigrated_from_chat_id()) { if (const auto migratedFrom = update.vmigrated_from_chat_id()) {

View file

@ -14,8 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer_bot_commands.h" #include "data/data_peer_bot_commands.h"
#include "data/data_user_names.h" #include "data/data_user_names.h"
class ChannelData;
struct ChannelLocation { struct ChannelLocation {
QString address; QString address;
Data::LocationPoint point; Data::LocationPoint point;
@ -72,7 +70,6 @@ enum class ChannelDataFlag : uint64 {
CanViewCreditsRevenue = (1ULL << 34), CanViewCreditsRevenue = (1ULL << 34),
SignatureProfiles = (1ULL << 35), SignatureProfiles = (1ULL << 35),
StargiftsAvailable = (1ULL << 36), StargiftsAvailable = (1ULL << 36),
PaidMessagesAvailable = (1ULL << 37),
}; };
inline constexpr bool is_flag_type(ChannelDataFlag) { return true; }; inline constexpr bool is_flag_type(ChannelDataFlag) { return true; };
using ChannelDataFlags = base::flags<ChannelDataFlag>; using ChannelDataFlags = base::flags<ChannelDataFlag>;
@ -153,9 +150,6 @@ private:
ChannelLocation _location; ChannelLocation _location;
Data::ChatBotCommands _botCommands; Data::ChatBotCommands _botCommands;
std::unique_ptr<Data::Forum> _forum; std::unique_ptr<Data::Forum> _forum;
int _starsPerMessage = 0;
friend class ChannelData;
}; };
@ -263,9 +257,6 @@ public:
[[nodiscard]] bool stargiftsAvailable() const { [[nodiscard]] bool stargiftsAvailable() const {
return flags() & Flag::StargiftsAvailable; return flags() & Flag::StargiftsAvailable;
} }
[[nodiscard]] bool paidMessagesAvailable() const {
return flags() & Flag::PaidMessagesAvailable;
}
[[nodiscard]] static ChatRestrictionsInfo KickedRestrictedRights( [[nodiscard]] static ChatRestrictionsInfo KickedRestrictedRights(
not_null<PeerData*> participant); not_null<PeerData*> participant);
@ -465,9 +456,6 @@ public:
[[nodiscard]] TimeId slowmodeLastMessage() const; [[nodiscard]] TimeId slowmodeLastMessage() const;
void growSlowmodeLastMessage(TimeId when); void growSlowmodeLastMessage(TimeId when);
void setStarsPerMessage(int stars);
[[nodiscard]] int starsPerMessage() const;
[[nodiscard]] int peerGiftsCount() const; [[nodiscard]] int peerGiftsCount() const;
void setPeerGiftsCount(int count); void setPeerGiftsCount(int count);

View file

@ -17,13 +17,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h" #include "data/data_user.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "ui/boxes/confirm_box.h"
#include "ui/chat/attach/attach_prepare.h" #include "ui/chat/attach/attach_prepare.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "styles/style_widgets.h"
namespace { namespace {
@ -123,7 +120,7 @@ bool CanSendAnyOf(
|| user->isRepliesChat() || user->isRepliesChat()
|| user->isVerifyCodes()) { || user->isVerifyCodes()) {
return false; return false;
} else if (user->requiresPremiumToWrite() } else if (user->meRequiresPremiumToWrite()
&& !user->session().premium()) { && !user->session().premium()) {
return false; return false;
} else if (rights } else if (rights
@ -180,7 +177,7 @@ SendError RestrictionError(
using Flag = ChatRestriction; using Flag = ChatRestriction;
if (const auto restricted = peer->amRestricted(restriction)) { if (const auto restricted = peer->amRestricted(restriction)) {
if (const auto user = peer->asUser()) { if (const auto user = peer->asUser()) {
if (user->requiresPremiumToWrite() if (user->meRequiresPremiumToWrite()
&& !user->session().premium()) { && !user->session().premium()) {
return SendError({ return SendError({
.text = tr::lng_restricted_send_non_premium( .text = tr::lng_restricted_send_non_premium(

View file

@ -66,11 +66,10 @@ struct CreditsHistoryEntry final {
uint64 bareGiftStickerId = 0; uint64 bareGiftStickerId = 0;
uint64 bareGiftOwnerId = 0; uint64 bareGiftOwnerId = 0;
uint64 bareActorId = 0; uint64 bareActorId = 0;
uint64 bareEntryOwnerId = 0; uint64 bareGiftListPeerId = 0;
uint64 giftChannelSavedId = 0; uint64 giftSavedId = 0;
uint64 stargiftId = 0; uint64 stargiftId = 0;
std::shared_ptr<UniqueGift> uniqueGift; std::shared_ptr<UniqueGift> uniqueGift;
Fn<std::vector<CreditsHistoryEntry>()> pinnedSavedGifts;
StarsAmount starrefAmount; StarsAmount starrefAmount;
int starrefCommission = 0; int starrefCommission = 0;
uint64 starrefRecipientId = 0; uint64 starrefRecipientId = 0;
@ -78,15 +77,11 @@ struct CreditsHistoryEntry final {
QDateTime subscriptionUntil; QDateTime subscriptionUntil;
QDateTime successDate; QDateTime successDate;
QString successLink; QString successLink;
int paidMessagesCount = 0;
StarsAmount paidMessagesAmount;
int paidMessagesCommission = 0;
int limitedCount = 0; int limitedCount = 0;
int limitedLeft = 0; int limitedLeft = 0;
int starsConverted = 0; int starsConverted = 0;
int starsToUpgrade = 0; int starsToUpgrade = 0;
int starsUpgradedBySender = 0; int starsUpgradedBySender = 0;
int premiumMonthsForStars = 0;
int floodSkip = 0; int floodSkip = 0;
bool converted : 1 = false; bool converted : 1 = false;
bool anonymous : 1 = false; bool anonymous : 1 = false;
@ -94,7 +89,6 @@ struct CreditsHistoryEntry final {
bool giftTransferred : 1 = false; bool giftTransferred : 1 = false;
bool giftRefunded : 1 = false; bool giftRefunded : 1 = false;
bool giftUpgraded : 1 = false; bool giftUpgraded : 1 = false;
bool giftPinned : 1 = false;
bool savedToProfile : 1 = false; bool savedToProfile : 1 = false;
bool fromGiftsList : 1 = false; bool fromGiftsList : 1 = false;
bool fromGiftSlug : 1 = false; bool fromGiftSlug : 1 = false;

View file

@ -16,10 +16,7 @@ namespace Data {
struct CreditsEarnStatistics final { struct CreditsEarnStatistics final {
explicit operator bool() const { explicit operator bool() const {
return usdRate return !!usdRate;
&& currentBalance
&& availableBalance
&& overallRevenue;
} }
Data::StatisticalGraph revenueGraph; Data::StatisticalGraph revenueGraph;
StarsAmount currentBalance; StarsAmount currentBalance;

View file

@ -50,7 +50,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "storage/file_download.h" #include "storage/file_download.h"
#include "storage/storage_account.h"
#include "storage/storage_facade.h" #include "storage/storage_facade.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
@ -66,28 +65,6 @@ using UpdateFlag = Data::PeerUpdate::Flag;
return session->appConfig().ignoredRestrictionReasons(); return session->appConfig().ignoredRestrictionReasons();
} }
[[nodiscard]] int ParseRegistrationDate(const QString &text) {
// MM.YYYY
if (text.size() != 7 || text[2] != '.') {
return 0;
}
const auto month = text.mid(0, 2).toInt();
const auto year = text.mid(3, 4).toInt();
return (year > 2012 && year < 2100 && month > 0 && month <= 12)
? (year * 100) + month
: 0;
}
[[nodiscard]] int RegistrationYear(int date) {
const auto year = date / 100;
return (year > 2012 && year < 2100) ? year : 0;
}
[[nodiscard]] int RegistrationMonth(int date) {
const auto month = date % 100;
return (month > 0 && month <= 12) ? month : 0;
}
} // namespace } // namespace
namespace Data { namespace Data {
@ -334,17 +311,6 @@ void PeerData::invalidateEmptyUserpic() {
_userpicEmpty = nullptr; _userpicEmpty = nullptr;
} }
void PeerData::checkTrustedPayForMessage() {
if (!_checkedTrustedPayForMessage
&& !starsPerMessage()
&& session().local().peerTrustedPayForMessageRead()) {
_checkedTrustedPayForMessage = 1;
if (session().local().hasPeerTrustedPayForMessageEntry(id)) {
session().local().clearPeerTrustedPayForMessage(id);
}
}
}
ClickHandlerPtr PeerData::createOpenLink() { ClickHandlerPtr PeerData::createOpenLink() {
return std::make_shared<PeerClickHandler>(this); return std::make_shared<PeerClickHandler>(this);
} }
@ -726,9 +692,7 @@ void PeerData::checkFolder(FolderId folderId) {
void PeerData::clearBusinessBot() { void PeerData::clearBusinessBot() {
if (const auto details = _barDetails.get()) { if (const auto details = _barDetails.get()) {
if (details->requestChatDate if (details->requestChatDate) {
|| details->paysPerMessage
|| !details->phoneCountryCode.isEmpty()) {
details->businessBot = nullptr; details->businessBot = nullptr;
details->businessBotManageUrl = QString(); details->businessBotManageUrl = QString();
} else { } else {
@ -771,27 +735,12 @@ void PeerData::saveTranslationDisabled(bool disabled) {
void PeerData::setBarSettings(const MTPPeerSettings &data) { void PeerData::setBarSettings(const MTPPeerSettings &data) {
data.match([&](const MTPDpeerSettings &data) { data.match([&](const MTPDpeerSettings &data) {
const auto wasPaysPerMessage = paysPerMessage(); if (!data.vbusiness_bot_id() && !data.vrequest_chat_title()) {
if (!data.vbusiness_bot_id()
&& !data.vrequest_chat_title()
&& !data.vcharge_paid_message_stars()
&& !data.vphone_country()
&& !data.vregistration_month()
&& !data.vname_change_date()
&& !data.vphoto_change_date()) {
_barDetails = nullptr; _barDetails = nullptr;
} else if (!_barDetails) { } else if (!_barDetails) {
_barDetails = std::make_unique<PeerBarDetails>(); _barDetails = std::make_unique<PeerBarDetails>();
} }
if (_barDetails) { if (_barDetails) {
_barDetails->phoneCountryCode
= qs(data.vphone_country().value_or_empty());
_barDetails->registrationDate = ParseRegistrationDate(
data.vregistration_month().value_or_empty());
_barDetails->nameChangeDate
= data.vname_change_date().value_or_empty();
_barDetails->photoChangeDate
= data.vphoto_change_date().value_or_empty();
_barDetails->requestChatTitle _barDetails->requestChatTitle
= qs(data.vrequest_chat_title().value_or_empty()); = qs(data.vrequest_chat_title().value_or_empty());
_barDetails->requestChatDate _barDetails->requestChatDate
@ -801,8 +750,6 @@ void PeerData::setBarSettings(const MTPPeerSettings &data) {
: nullptr; : nullptr;
_barDetails->businessBotManageUrl _barDetails->businessBotManageUrl
= qs(data.vbusiness_bot_manage_url().value_or_empty()); = qs(data.vbusiness_bot_manage_url().value_or_empty());
_barDetails->paysPerMessage
= data.vcharge_paid_message_stars().value_or_empty();
} }
using Flag = PeerBarSetting; using Flag = PeerBarSetting;
setBarSettings((data.is_add_contact() ? Flag::AddContact : Flag()) setBarSettings((data.is_add_contact() ? Flag::AddContact : Flag())
@ -826,35 +773,8 @@ void PeerData::setBarSettings(const MTPPeerSettings &data) {
| (data.is_business_bot_can_reply() | (data.is_business_bot_can_reply()
? Flag::BusinessBotCanReply ? Flag::BusinessBotCanReply
: Flag())); : Flag()));
if (wasPaysPerMessage != paysPerMessage()) {
session().changes().peerUpdated(
this,
UpdateFlag::PaysPerMessage);
}
}); });
} }
int PeerData::paysPerMessage() const {
return _barDetails ? _barDetails->paysPerMessage : 0;
}
void PeerData::clearPaysPerMessage() {
if (const auto details = _barDetails.get()) {
if (details->paysPerMessage) {
if (details->businessBot
|| details->requestChatDate
|| !details->phoneCountryCode.isEmpty()) {
details->paysPerMessage = 0;
} else {
_barDetails = nullptr;
}
session().changes().peerUpdated(
this,
UpdateFlag::PaysPerMessage);
}
}
}
QString PeerData::requestChatTitle() const { QString PeerData::requestChatTitle() const {
return _barDetails ? _barDetails->requestChatTitle : QString(); return _barDetails ? _barDetails->requestChatTitle : QString();
} }
@ -871,28 +791,6 @@ QString PeerData::businessBotManageUrl() const {
return _barDetails ? _barDetails->businessBotManageUrl : QString(); return _barDetails ? _barDetails->businessBotManageUrl : QString();
} }
QString PeerData::phoneCountryCode() const {
return _barDetails ? _barDetails->phoneCountryCode : QString();
}
int PeerData::registrationMonth() const {
return _barDetails
? RegistrationMonth(_barDetails->registrationDate)
: 0;
}
int PeerData::registrationYear() const {
return _barDetails ? RegistrationYear(_barDetails->registrationDate) : 0;
}
TimeId PeerData::nameChangeDate() const {
return _barDetails ? _barDetails->nameChangeDate : 0;
}
TimeId PeerData::photoChangeDate() const {
return _barDetails ? _barDetails->photoChangeDate : 0;
}
bool PeerData::changeColorIndex( bool PeerData::changeColorIndex(
const tl::conditional<MTPint> &cloudColorIndex) { const tl::conditional<MTPint> &cloudColorIndex) {
return cloudColorIndex return cloudColorIndex
@ -1403,7 +1301,7 @@ Data::RestrictionCheckResult PeerData::amRestricted(
} }
}; };
if (const auto user = asUser()) { if (const auto user = asUser()) {
if (user->requiresPremiumToWrite() && !user->session().premium()) { if (user->meRequiresPremiumToWrite() && !user->session().premium()) {
return Result::Explicit(); return Result::Explicit();
} }
return (right == ChatRestriction::SendVoiceMessages return (right == ChatRestriction::SendVoiceMessages
@ -1522,24 +1420,6 @@ bool PeerData::canManageGroupCall() const {
return false; return false;
} }
int PeerData::starsPerMessage() const {
if (const auto user = asUser()) {
return user->starsPerMessage();
} else if (const auto channel = asChannel()) {
return channel->starsPerMessage();
}
return 0;
}
int PeerData::starsPerMessageChecked() const {
if (const auto channel = asChannel()) {
return (channel->adminRights() || channel->amCreator())
? 0
: channel->starsPerMessage();
}
return starsPerMessage();
}
Data::GroupCall *PeerData::groupCall() const { Data::GroupCall *PeerData::groupCall() const {
if (const auto chat = asChat()) { if (const auto chat = asChat()) {
return chat->groupCall(); return chat->groupCall();

View file

@ -173,15 +173,10 @@ inline constexpr bool is_flag_type(PeerBarSetting) { return true; };
using PeerBarSettings = base::flags<PeerBarSetting>; using PeerBarSettings = base::flags<PeerBarSetting>;
struct PeerBarDetails { struct PeerBarDetails {
QString phoneCountryCode;
int registrationDate = 0; // YYYYMM or 0, YYYY > 2012, MM > 0.
TimeId nameChangeDate = 0;
TimeId photoChangeDate = 0;
QString requestChatTitle; QString requestChatTitle;
TimeId requestChatDate; TimeId requestChatDate;
UserData *businessBot = nullptr; UserData *businessBot = nullptr;
QString businessBotManageUrl; QString businessBotManageUrl;
int paysPerMessage = 0;
}; };
class PeerData { class PeerData {
@ -273,9 +268,6 @@ public:
[[nodiscard]] int slowmodeSecondsLeft() const; [[nodiscard]] int slowmodeSecondsLeft() const;
[[nodiscard]] bool canManageGroupCall() const; [[nodiscard]] bool canManageGroupCall() const;
[[nodiscard]] int starsPerMessage() const;
[[nodiscard]] int starsPerMessageChecked() const;
[[nodiscard]] UserData *asBot(); [[nodiscard]] UserData *asBot();
[[nodiscard]] const UserData *asBot() const; [[nodiscard]] const UserData *asBot() const;
[[nodiscard]] UserData *asUser(); [[nodiscard]] UserData *asUser();
@ -417,18 +409,11 @@ public:
? _barSettings.changes() ? _barSettings.changes()
: (_barSettings.value() | rpl::type_erased()); : (_barSettings.value() | rpl::type_erased());
} }
[[nodiscard]] int paysPerMessage() const;
void clearPaysPerMessage();
[[nodiscard]] QString requestChatTitle() const; [[nodiscard]] QString requestChatTitle() const;
[[nodiscard]] TimeId requestChatDate() const; [[nodiscard]] TimeId requestChatDate() const;
[[nodiscard]] UserData *businessBot() const; [[nodiscard]] UserData *businessBot() const;
[[nodiscard]] QString businessBotManageUrl() const; [[nodiscard]] QString businessBotManageUrl() const;
void clearBusinessBot(); void clearBusinessBot();
[[nodiscard]] QString phoneCountryCode() const;
[[nodiscard]] int registrationMonth() const;
[[nodiscard]] int registrationYear() const;
[[nodiscard]] TimeId nameChangeDate() const;
[[nodiscard]] TimeId photoChangeDate() const;
enum class TranslationFlag : uchar { enum class TranslationFlag : uchar {
Unknown, Unknown,
@ -516,7 +501,6 @@ protected:
void updateUserpic(PhotoId photoId, MTP::DcId dcId, bool hasVideo); void updateUserpic(PhotoId photoId, MTP::DcId dcId, bool hasVideo);
void clearUserpic(); void clearUserpic();
void invalidateEmptyUserpic(); void invalidateEmptyUserpic();
void checkTrustedPayForMessage();
private: private:
void fillNames(); void fillNames();
@ -551,10 +535,9 @@ private:
crl::time _lastFullUpdate = 0; crl::time _lastFullUpdate = 0;
QString _name; QString _name;
uint32 _nameVersion : 29 = 1; uint32 _nameVersion : 30 = 1;
uint32 _sensitiveContent : 1 = 0; uint32 _sensitiveContent : 1 = 0;
uint32 _wallPaperOverriden : 1 = 0; uint32 _wallPaperOverriden : 1 = 0;
uint32 _checkedTrustedPayForMessage : 1 = 0;
TimeId _ttlPeriod = 0; TimeId _ttlPeriod = 0;

View file

@ -228,11 +228,11 @@ inline auto DefaultRestrictionValue(
| ChatRestriction::SendVideoMessages); | ChatRestriction::SendVideoMessages);
auto allowedAny = PeerFlagsValue( auto allowedAny = PeerFlagsValue(
user, user,
(UserDataFlag::Deleted | UserDataFlag::RequiresPremiumToWrite) (UserDataFlag::Deleted | UserDataFlag::MeRequiresPremiumToWrite)
) | rpl::map([=](UserDataFlags flags) { ) | rpl::map([=](UserDataFlags flags) {
return (flags & UserDataFlag::Deleted) return (flags & UserDataFlag::Deleted)
? rpl::single(false) ? rpl::single(false)
: !(flags & UserDataFlag::RequiresPremiumToWrite) : !(flags & UserDataFlag::MeRequiresPremiumToWrite)
? rpl::single(true) ? rpl::single(true)
: AmPremiumValue(&user->session()); : AmPremiumValue(&user->session());
}) | rpl::flatten_latest(); }) | rpl::flatten_latest();

Some files were not shown because too many files have changed in this diff Show more