Merge tag 'v4.15.2' into dev

# Conflicts:
#	Telegram/Resources/winrc/Telegram.rc
#	Telegram/Resources/winrc/Updater.rc
#	Telegram/SourceFiles/core/version.h
#	Telegram/SourceFiles/info/info_wrap_widget.cpp
#	Telegram/SourceFiles/settings/settings_common_session.cpp
#	Telegram/lib_ui
#	docs/building-mac.md
#	snap/snapcraft.yaml
This commit is contained in:
AlexeyZavar 2024-03-13 11:44:09 +03:00
commit 080400d7b8
288 changed files with 12133 additions and 2889 deletions

View file

@ -64,7 +64,7 @@ jobs:
- name: First set up. - name: First set up.
run: | run: |
sudo chown -R `whoami`:admin /usr/local/share sudo chown -R `whoami`:admin /usr/local/share
brew install automake ninja pkg-config brew install automake ninja pkg-config nasm meson
# Disable spotlight. # Disable spotlight.
sudo mdutil -a -i off sudo mdutil -a -i off

3
.gitmodules vendored
View file

@ -100,3 +100,6 @@
[submodule "Telegram/ThirdParty/libprisma"] [submodule "Telegram/ThirdParty/libprisma"]
path = Telegram/ThirdParty/libprisma path = Telegram/ThirdParty/libprisma
url = https://github.com/desktop-app/libprisma.git url = https://github.com/desktop-app/libprisma.git
[submodule "Telegram/ThirdParty/xdg-desktop-portal"]
path = Telegram/ThirdParty/xdg-desktop-portal
url = https://github.com/flatpak/xdg-desktop-portal.git

View file

@ -248,6 +248,8 @@ PRIVATE
boxes/filters/edit_filter_box.h boxes/filters/edit_filter_box.h
boxes/filters/edit_filter_chats_list.cpp boxes/filters/edit_filter_chats_list.cpp
boxes/filters/edit_filter_chats_list.h boxes/filters/edit_filter_chats_list.h
boxes/filters/edit_filter_chats_preview.cpp
boxes/filters/edit_filter_chats_preview.h
boxes/filters/edit_filter_links.cpp boxes/filters/edit_filter_links.cpp
boxes/filters/edit_filter_links.h boxes/filters/edit_filter_links.h
boxes/peers/add_bot_to_chat_box.cpp boxes/peers/add_bot_to_chat_box.cpp
@ -514,6 +516,14 @@ PRIVATE
core/version.h core/version.h
countries/countries_manager.cpp countries/countries_manager.cpp
countries/countries_manager.h countries/countries_manager.h
data/business/data_business_chatbots.cpp
data/business/data_business_chatbots.h
data/business/data_business_common.cpp
data/business/data_business_common.h
data/business/data_business_info.cpp
data/business/data_business_info.h
data/business/data_shortcut_messages.cpp
data/business/data_shortcut_messages.h
data/notify/data_notify_settings.cpp data/notify/data_notify_settings.cpp
data/notify/data_notify_settings.h data/notify/data_notify_settings.h
data/notify/data_peer_notify_settings.cpp data/notify/data_peer_notify_settings.cpp
@ -1345,6 +1355,22 @@ PRIVATE
profile/profile_block_widget.h profile/profile_block_widget.h
profile/profile_cover_drop_area.cpp profile/profile_cover_drop_area.cpp
profile/profile_cover_drop_area.h profile/profile_cover_drop_area.h
settings/business/settings_away_message.cpp
settings/business/settings_away_message.h
settings/business/settings_shortcut_messages.cpp
settings/business/settings_shortcut_messages.h
settings/business/settings_chatbots.cpp
settings/business/settings_chatbots.h
settings/business/settings_greeting.cpp
settings/business/settings_greeting.h
settings/business/settings_location.cpp
settings/business/settings_location.h
settings/business/settings_quick_replies.cpp
settings/business/settings_quick_replies.h
settings/business/settings_recipients_helper.cpp
settings/business/settings_recipients_helper.h
settings/business/settings_working_hours.cpp
settings/business/settings_working_hours.h
settings/cloud_password/settings_cloud_password_common.cpp settings/cloud_password/settings_cloud_password_common.cpp
settings/cloud_password/settings_cloud_password_common.h settings/cloud_password/settings_cloud_password_common.h
settings/cloud_password/settings_cloud_password_email.cpp settings/cloud_password/settings_cloud_password_email.cpp
@ -1363,6 +1389,8 @@ PRIVATE
settings/settings_advanced.h settings/settings_advanced.h
settings/settings_blocked_peers.cpp settings/settings_blocked_peers.cpp
settings/settings_blocked_peers.h settings/settings_blocked_peers.h
settings/settings_business.cpp
settings/settings_business.h
settings/settings_chat.cpp settings/settings_chat.cpp
settings/settings_chat.h settings/settings_chat.h
settings/settings_calls.cpp settings/settings_calls.cpp
@ -1722,7 +1750,7 @@ else()
) )
include(${cmake_helpers_loc}/external/glib/generate_dbus.cmake) include(${cmake_helpers_loc}/external/glib/generate_dbus.cmake)
generate_dbus(Telegram org.freedesktop.portal. XdpInhibit ${src_loc}/platform/linux/org.freedesktop.portal.Inhibit.xml) generate_dbus(Telegram org.freedesktop.portal. XdpBackground ${third_party_loc}/xdg-desktop-portal/data/org.freedesktop.portal.Background.xml)
if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION) if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
target_link_libraries(Telegram target_link_libraries(Telegram
@ -1796,6 +1824,7 @@ set_target_properties(Telegram PROPERTIES
XCODE_ATTRIBUTE_ALWAYS_SEARCH_USER_PATHS NO XCODE_ATTRIBUTE_ALWAYS_SEARCH_USER_PATHS NO
XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY libc++ XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY libc++
XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS --deep XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS --deep
XCODE_ATTRIBUTE_CLANG_DEBUG_INFORMATION_LEVEL $<IF:$<CONFIG:Debug>,default,line-tables-only>
) )
set(entitlement_sources set(entitlement_sources
"${CMAKE_CURRENT_SOURCE_DIR}/Telegram/Telegram.entitlements" "${CMAKE_CURRENT_SOURCE_DIR}/Telegram/Telegram.entitlements"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View file

@ -559,3 +559,26 @@ div.toast_shown {
opacity: 0; opacity: 0;
user-select: none; user-select: none;
} }
.bot_buttons_table {
border-spacing: 0px 2px;
width: 100%;
}
.bot_button {
border-radius: 8px;
text-align: center;
vertical-align: middle;
background-color: #168acd40;
}
.bot_button_row {
display: table;
table-layout: fixed;
padding: 0px;
width:100%;
}
.bot_button_row div {
display: table-cell;
}
.bot_button_column_separator {
width: 2px
}

View file

@ -62,6 +62,12 @@ function ShowNotAvailableEmoji() {
return false; return false;
} }
function ShowTextCopied(content) {
navigator.clipboard.writeText(content);
ShowToast("Text copied to clipboard.");
return false;
}
function ShowSpoiler(target) { function ShowSpoiler(target) {
if (target.classList.contains("hidden")) { if (target.classList.contains("hidden")) {
target.classList.toggle("hidden"); target.classList.toggle("hidden");

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 955 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 497 B

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 787 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1313,6 +1313,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_info_link_label" = "Link"; "lng_info_link_label" = "Link";
"lng_info_location_label" = "Location"; "lng_info_location_label" = "Location";
"lng_info_about_label" = "About"; "lng_info_about_label" = "About";
"lng_info_work_open" = "Open";
"lng_info_work_closed" = "Closed";
"lng_info_hours_label" = "Business hours";
"lng_info_hours_closed" = "closed";
"lng_info_hours_opens_in_minutes#one" = "opens in {count} minute";
"lng_info_hours_opens_in_minutes#other" = "opens in {count} minutes";
"lng_info_hours_opens_in_hours#one" = "opens in {count} hour";
"lng_info_hours_opens_in_hours#other" = "opens in {count} hours";
"lng_info_hours_opens_in_days#one" = "opens in {count} day";
"lng_info_hours_opens_in_days#other" = "opens in {count} days";
"lng_info_hours_open_full" = "open 24 hours";
"lng_info_hours_next_day" = "{time} (next day)";
"lng_info_hours_local_time" = "local time";
"lng_info_hours_my_time" = "my time";
"lng_info_user_title" = "User Info"; "lng_info_user_title" = "User Info";
"lng_info_bot_title" = "Bot Info"; "lng_info_bot_title" = "Bot Info";
"lng_info_group_title" = "Group Info"; "lng_info_group_title" = "Group Info";
@ -2056,6 +2070,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_premium_summary_about_animated_userpics" = "Video avatars animated in chat lists and chats to allow for additional self-expression."; "lng_premium_summary_about_animated_userpics" = "Video avatars animated in chat lists and chats to allow for additional self-expression.";
"lng_premium_summary_subtitle_translation" = "Real-Time Translation"; "lng_premium_summary_subtitle_translation" = "Real-Time Translation";
"lng_premium_summary_about_translation" = "Real-time translation of channels and chats into other languages."; "lng_premium_summary_about_translation" = "Real-time translation of channels and chats into other languages.";
"lng_premium_summary_subtitle_business" = "Telegram Business";
"lng_premium_summary_about_business" = "Upgrade your account with business features such as location, opening hours and quick replies.";
"lng_premium_summary_bottom_subtitle" = "About Telegram Premium"; "lng_premium_summary_bottom_subtitle" = "About Telegram Premium";
"lng_premium_summary_bottom_about" = "While the free version of Telegram already gives its users more than any other messaging application, **Telegram Premium** pushes its capabilities even further.\n\n**Telegram Premium** is a paid option, because most Premium Features require additional expenses from Telegram to third parties such as data center providers and server manufacturers. Contributions from **Telegram Premium** users allow us to cover such costs and also help Telegram stay free for everyone."; "lng_premium_summary_bottom_about" = "While the free version of Telegram already gives its users more than any other messaging application, **Telegram Premium** pushes its capabilities even further.\n\n**Telegram Premium** is a paid option, because most Premium Features require additional expenses from Telegram to third parties such as data center providers and server manufacturers. Contributions from **Telegram Premium** users allow us to cover such costs and also help Telegram stay free for everyone.";
"lng_premium_summary_button" = "Subscribe for {cost} per month"; "lng_premium_summary_button" = "Subscribe for {cost} per month";
@ -2154,6 +2170,125 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_premium_gifts_terms" = "By gifting Telegram Premium, you agree to the Telegram {link} and {policy}."; "lng_premium_gifts_terms" = "By gifting Telegram Premium, you agree to the Telegram {link} and {policy}.";
"lng_premium_gifts_terms_policy" = "Privacy Policy"; "lng_premium_gifts_terms_policy" = "Privacy Policy";
"lng_business_title" = "Telegram Business";
"lng_business_about" = "Turn your account to a business page with these additional features.";
"lng_business_unlocked" = "You have now unlocked these additional business features.";
"lng_business_subtitle_location" = "Location";
"lng_business_about_location" = "Display the location of your business on your account.";
"lng_business_subtitle_opening_hours" = "Opening Hours";
"lng_business_about_opening_hours" = "Show to your customers when you are open for business.";
"lng_business_subtitle_quick_replies" = "Quick Replies";
"lng_business_about_quick_replies" = "Set up shortcuts up to 20 messages each to respond to customers faster.";
"lng_business_subtitle_greeting_messages" = "Greeting Messages";
"lng_business_about_greeting_messages" = "Create greetings that will be automatically sent to new customers.";
"lng_business_subtitle_away_messages" = "Away Messages";
"lng_business_about_away_messages" = "Define messages that are automatically sent when you are off.";
"lng_business_subtitle_chatbots" = "Chatbots";
"lng_business_about_chatbots" = "Add any third party chatbots that will process customer interactions.";
"lng_location_title" = "Location";
"lng_location_about" = "Display the location of your business on your account.";
"lng_location_address" = "Enter Address";
"lng_location_fallback" = "You can set your location on the map from your mobile device.";
"lng_hours_title" = "Business Hours";
"lng_hours_about" = "Turn this on to show your opening hours schedule to your customers.";
"lng_hours_show" = "Show Business Hours";
"lng_hours_time_zone" = "Time Zone";
"lng_hours_monday" = "Monday";
"lng_hours_tuesday" = "Tuesday";
"lng_hours_wednesday" = "Wednesday";
"lng_hours_thursday" = "Thursday";
"lng_hours_friday" = "Friday";
"lng_hours_saturday" = "Saturday";
"lng_hours_sunday" = "Sunday";
"lng_hours_closed" = "Closed";
"lng_hours_open_full" = "Open 24 hours";
"lng_hours_next_day" = "{time} (Next day)";
"lng_hours_on_next_day" = "Next day {time}";
"lng_hours_time_zone_title" = "Choose Time Zone";
"lng_hours_add_button" = "Add a Set of Hours";
"lng_hours_opening" = "Opening Time";
"lng_hours_closing" = "Closing Time";
"lng_hours_remove" = "Remove";
"lng_hours_about_day" = "Specify your working hours during the day.";
"lng_replies_title" = "Quick Replies";
"lng_replies_about" = "Set up shortcuts with rich text and media to respond to messages faster.";
"lng_replies_add" = "Add Quick Reply";
"lng_replies_add_title" = "New Quick Reply";
"lng_replies_add_shortcut" = "Add a shortcut for your reply.";
"lng_replies_add_placeholder" = "Shortcut";
"lng_replies_add_exists" = "This shortcut already exists.";
"lng_replies_empty_title" = "New Quick Reply";
"lng_replies_empty_about" = "Enter a message below that will be sent in chat when you type {shortcut}.\n\nYou can access Quick Replies in any chat by typing /.";
"lng_replies_remove_title" = "Remove Shortcut";
"lng_replies_remove_text" = "You didn't create a quick reply message. Do you want to remove the shortcut?";
"lng_replies_edit_title" = "Edit Shortcut";
"lng_replies_edit_about" = "Edit the name for this shortcut.";
"lng_replies_message_placeholder" = "Add a Quick Reply";
"lng_replies_delete_sure" = "Are you sure you want to delete this quick reply with all its messages?";
"lng_replies_error_occupied" = "This shortcut is already used.";
"lng_replies_edit_button" = "Edit Quick Replies";
"lng_greeting_title" = "Greeting Message";
"lng_greeting_about" = "Greet customers when they message you the first time or after a period of no activity.";
"lng_greeting_enable" = "Send Greeting Message";
"lng_greeting_create" = "Create a Greeting Message";
"lng_greeting_recipients" = "Recipients";
"lng_greeting_select" = "Select chats or entire chat categories for sending a greeting message.";
"lng_greeting_period_title" = "Period of no activity";
"lng_greeting_period_about" = "Choose how many days should pass after your last interaction with a recipient to send them a greeting in response to their message.";
"lng_greeting_empty_title" = "New Greeting Message";
"lng_greeting_empty_about" = "Create greetings that will be automatically sent to new customers.";
"lng_greeting_message_placeholder" = "Add a Greeting";
"lng_greeting_limit_reached" = "You have too many quick replies. Remove one to add a greeting message.";
"lng_greeting_recipients_empty" = "Please choose at least one recipient.";
"lng_away_title" = "Away Message";
"lng_away_about" = "Automatically reply with a message when you are away.";
"lng_away_enable" = "Send Away Message";
"lng_away_create" = "Create an Away Message";
"lng_away_schedule" = "Schedule";
"lng_away_schedule_always" = "Send Always";
"lng_away_schedule_outside" = "Outside of Business Hours";
"lng_away_schedule_custom" = "Custom Schedule";
"lng_away_custom_start" = "Start Time";
"lng_away_custom_end" = "End Time";
"lng_away_offline_only" = "Only if Offline";
"lng_away_offline_only_about" = "Don't send the away message if you've recently been online.";
"lng_away_recipients" = "Recipients";
"lng_away_select" = "Select chats or entire chat categories for sending an away message.";
"lng_away_empty_title" = "New Away Message";
"lng_away_empty_about" = "Add messages that will be automatically sent when you are off.";
"lng_away_message_placeholder" = "Add an Away Message";
"lng_away_limit_reached" = "You have too many quick replies. Remove one to add an away message.";
"lng_business_edit_messages" = "Edit messages";
"lng_business_limit_reached#one" = "Limit of {count} message reached.";
"lng_business_limit_reached#other" = "Limit of {count} messages reached.";
"lng_chatbots_title" = "Chatbots";
"lng_chatbots_about" = "Add a bot to your account to help you automatically process and respond to the messages you receive. {link}";
"lng_chatbots_about_link" = "Learn more...";
"lng_chatbots_placeholder" = "Enter bot URL or username";
"lng_chatbots_add_about" = "Enter the link to the Telegram bot that you want to automatically process your chats.";
"lng_chatbots_access_title" = "Chats accessible for the bot";
"lng_chatbots_all_except" = "All 1-to-1 Chats Except...";
"lng_chatbots_selected" = "Only Selected Chats";
"lng_chatbots_excluded_title" = "Excluded chats";
"lng_chatbots_exclude_button" = "Exclude Chats";
"lng_chatbots_included_title" = "Included chats";
"lng_chatbots_include_button" = "Select Chats";
"lng_chatbots_exclude_about" = "Select chats or entire chat categories which the bot will not have access to.";
"lng_chatbots_permissions_title" = "Bot permissions";
"lng_chatbots_reply" = "Reply to Messages";
"lng_chatbots_reply_about" = "The bot will be able to view all new incoming messages, but not the messages that had been sent before you added the bot.";
"lng_chatbots_remove" = "Remove Bot";
"lng_chatbots_not_found" = "Chatbot not found.";
"lng_chatbots_add" = "Add";
"lng_chatbots_info_url" = "https://telegram.org/privacy";
"lng_boost_channel_button" = "Boost Channel"; "lng_boost_channel_button" = "Boost Channel";
"lng_boost_group_button" = "Boost Group"; "lng_boost_group_button" = "Boost Group";
"lng_boost_again_button" = "Boost Again"; "lng_boost_again_button" = "Boost Again";
@ -2917,6 +3052,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_translate_selected" = "Translate Selected Text"; "lng_context_translate_selected" = "Translate Selected Text";
"lng_context_read_hidden" = "read"; "lng_context_read_hidden" = "read";
"lng_context_read_show" = "show when"; "lng_context_read_show" = "show when";
"lng_context_edit_shortcut" = "Edit Shortcut";
"lng_context_delete_shortcut" = "Delete Quick Reply";
"lng_add_tag_about" = "Tag this message with an emoji for quick search."; "lng_add_tag_about" = "Tag this message with an emoji for quick search.";
"lng_subscribe_tag_about" = "Organize your Saved Messages with tags. {link}"; "lng_subscribe_tag_about" = "Organize your Saved Messages with tags. {link}";
@ -4305,6 +4442,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_filters_type_non_contacts" = "Non-Contacts"; "lng_filters_type_non_contacts" = "Non-Contacts";
"lng_filters_type_groups" = "Groups"; "lng_filters_type_groups" = "Groups";
"lng_filters_type_channels" = "Channels"; "lng_filters_type_channels" = "Channels";
"lng_filters_type_new" = "New Chats";
"lng_filters_type_existing" = "Existing Chats";
"lng_filters_type_bots" = "Bots"; "lng_filters_type_bots" = "Bots";
"lng_filters_type_no_archived" = "Archived"; "lng_filters_type_no_archived" = "Archived";
"lng_filters_type_no_muted" = "Muted"; "lng_filters_type_no_muted" = "Muted";
@ -4724,6 +4863,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_boosts_prepaid_giveaway_status#one" = "{count} subscription {duration}"; "lng_boosts_prepaid_giveaway_status#one" = "{count} subscription {duration}";
"lng_boosts_prepaid_giveaway_status#other" = "{count} subscriptions {duration}"; "lng_boosts_prepaid_giveaway_status#other" = "{count} subscriptions {duration}";
"lng_contact_add" = "Add";
"lng_contact_send_message" = "message";
// Wnd specific // Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program..."; "lng_wnd_choose_program_menu" = "Choose Default Program...";

View file

@ -14,5 +14,12 @@
<file alias="voice_ttl_idle.tgs">../../animations/voice_ttl_idle.tgs</file> <file alias="voice_ttl_idle.tgs">../../animations/voice_ttl_idle.tgs</file>
<file alias="voice_ttl_start.tgs">../../animations/voice_ttl_start.tgs</file> <file alias="voice_ttl_start.tgs">../../animations/voice_ttl_start.tgs</file>
<file alias="palette.tgs">../../animations/palette.tgs</file> <file alias="palette.tgs">../../animations/palette.tgs</file>
<file alias="sleep.tgs">../../animations/sleep.tgs</file>
<file alias="greeting.tgs">../../animations/greeting.tgs</file>
<file alias="location.tgs">../../animations/location.tgs</file>
<file alias="robot.tgs">../../animations/robot.tgs</file>
<file alias="writing.tgs">../../animations/writing.tgs</file>
<file alias="hours.tgs">../../animations/hours.tgs</file>
<file alias="phone.tgs">../../animations/phone.tgs</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -3,6 +3,7 @@
<file alias="art/background.tgv">../../art/background.tgv</file> <file alias="art/background.tgv">../../art/background.tgv</file>
<file alias="art/bg_thumbnail.png">../../art/bg_thumbnail.png</file> <file alias="art/bg_thumbnail.png">../../art/bg_thumbnail.png</file>
<file alias="art/bg_initial.jpg">../../art/bg_initial.jpg</file> <file alias="art/bg_initial.jpg">../../art/bg_initial.jpg</file>
<file alias="art/business_logo.png">../../art/business_logo.png</file>
<file alias="art/logo_256.png">../../art/logo_256.png</file> <file alias="art/logo_256.png">../../art/logo_256.png</file>
<file alias="art/logo_256_no_margin.png">../../art/logo_256_no_margin.png</file> <file alias="art/logo_256_no_margin.png">../../art/logo_256_no_margin.png</file>
<file alias="art/themeimage.jpg">../../art/themeimage.jpg</file> <file alias="art/themeimage.jpg">../../art/themeimage.jpg</file>

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="4.15.0.0" /> Version="4.15.2.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 4,15,0,0 FILEVERSION 4,15,2,0
PRODUCTVERSION 4,15,0,0 PRODUCTVERSION 4,15,2,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", "4.15.0.0" VALUE "FileVersion", "4.15.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "AyuGram Desktop" VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "4.15.0.0" VALUE "ProductVersion", "4.15.2.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 4,15,0,0 FILEVERSION 4,15,2,0
PRODUCTVERSION 4,15,0,0 PRODUCTVERSION 4,15,2,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", "4.15.0.0" VALUE "FileVersion", "4.15.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "AyuGram Desktop" VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "4.15.0.0" VALUE "ProductVersion", "4.15.2.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -22,6 +22,7 @@ inline constexpr auto kScheduledUntilOnlineTimestamp = TimeId(0x7FFFFFFE);
struct SendOptions { struct SendOptions {
PeerData *sendAs = nullptr; PeerData *sendAs = nullptr;
TimeId scheduled = 0; TimeId scheduled = 0;
BusinessShortcutId shortcutId = 0;
bool silent = false; bool silent = false;
bool handleSupportSwitch = false; bool handleSupportSwitch = false;
bool hideViaBot = false; bool hideViaBot = false;

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_media.h" #include "api/api_media.h"
#include "api/api_text_entities.h" #include "api/api_text_entities.h"
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "data/business/data_shortcut_messages.h"
#include "data/data_histories.h" #include "data/data_histories.h"
#include "data/data_scheduled_messages.h" #include "data/data_scheduled_messages.h"
#include "data/data_session.h" #include "data/data_session.h"
@ -88,10 +89,15 @@ mtpRequestId EditMessage(
: emptyFlag) : emptyFlag)
| (options.scheduled | (options.scheduled
? MTPmessages_EditMessage::Flag::f_schedule_date ? MTPmessages_EditMessage::Flag::f_schedule_date
: emptyFlag)
| (item->isBusinessShortcut()
? MTPmessages_EditMessage::Flag::f_quick_reply_shortcut_id
: emptyFlag); : emptyFlag);
const auto id = item->isScheduled() const auto id = item->isScheduled()
? session->data().scheduledMessages().lookupId(item) ? session->data().scheduledMessages().lookupId(item)
: item->isBusinessShortcut()
? session->data().shortcutMessages().lookupId(item)
: item->id; : item->id;
return api->request(MTPmessages_EditMessage( return api->request(MTPmessages_EditMessage(
MTP_flags(flags), MTP_flags(flags),
@ -101,7 +107,8 @@ mtpRequestId EditMessage(
inputMedia.value_or(Data::WebPageForMTP(webpage, text.isEmpty())), inputMedia.value_or(Data::WebPageForMTP(webpage, text.isEmpty())),
MTPReplyMarkup(), MTPReplyMarkup(),
sentEntities, sentEntities,
MTP_int(options.scheduled) MTP_int(options.scheduled),
MTP_int(item->shortcutId())
)).done([=]( )).done([=](
const MTPUpdates &result, const MTPUpdates &result,
[[maybe_unused]] mtpRequestId requestId) { [[maybe_unused]] mtpRequestId requestId) {

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_updates.h" #include "api/api_updates.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "base/random.h" #include "base/random.h"
#include "data/business/data_shortcut_messages.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_histories.h" #include "data/data_histories.h"
#include "data/data_poll.h" #include "data/data_poll.h"
@ -69,6 +70,9 @@ void Polls::create(
if (action.options.scheduled) { if (action.options.scheduled) {
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date; sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
} }
if (action.options.shortcutId) {
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
}
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;
@ -89,7 +93,8 @@ void Polls::create(
MTPReplyMarkup(), MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(), MTPVector<MTPMessageEntity>(),
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)
), [=](const MTPUpdates &result, const MTP::Response &response) { ), [=](const MTPUpdates &result, const MTP::Response &response) {
if (clearCloudDraft) { if (clearCloudDraft) {
history->finishSavingCloudDraft( history->finishSavingCloudDraft(
@ -184,7 +189,8 @@ void Polls::close(not_null<HistoryItem*> item) {
PollDataToInputMedia(poll, true), PollDataToInputMedia(poll, true),
MTPReplyMarkup(), MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(), MTPVector<MTPMessageEntity>(),
MTP_int(0) // schedule_date MTP_int(0), // schedule_date
MTPint() // quick_reply_shortcut_id
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
_pollCloseRequestIds.erase(itemId); _pollCloseRequestIds.erase(itemId);
_session->updates().applyUpdates(result); _session->updates().applyUpdates(result);

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_text_entities.h" #include "api/api_text_entities.h"
#include "base/random.h" #include "base/random.h"
#include "base/unixtime.h" #include "base/unixtime.h"
#include "data/business/data_shortcut_messages.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_channel.h" // ChannelData::addsSignature. #include "data/data_channel.h" // ChannelData::addsSignature.
@ -84,20 +85,21 @@ void SendExistingMedia(
? (*localMessageId) ? (*localMessageId)
: session->data().nextLocalMessageId()); : session->data().nextLocalMessageId());
const auto randomId = base::RandomValue<uint64>(); const auto randomId = base::RandomValue<uint64>();
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);
if (message.action.replyTo) { if (action.replyTo) {
flags |= MessageFlag::HasReplyInfo; flags |= MessageFlag::HasReplyInfo;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to; sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
} }
const auto anonymousPost = peer->amAnonymous(); const auto anonymousPost = peer->amAnonymous();
const auto silentPost = ShouldSendSilent(peer, message.action.options); const auto silentPost = ShouldSendSilent(peer, action.options);
InnerFillMessagePostFlags(message.action.options, peer, flags); InnerFillMessagePostFlags(action.options, peer, flags);
if (silentPost) { if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent; sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
} }
const auto sendAs = message.action.options.sendAs; const auto sendAs = action.options.sendAs;
const auto messageFromId = sendAs const auto messageFromId = sendAs
? sendAs->id ? sendAs->id
: anonymousPost : anonymousPost
@ -124,32 +126,34 @@ void SendExistingMedia(
} }
const auto captionText = caption.text; const auto captionText = caption.text;
if (message.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;
} }
if (action.options.shortcutId) {
flags |= MessageFlag::ShortcutMessage;
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
}
session->data().registerMessageRandomId(randomId, newId); session->data().registerMessageRandomId(randomId, newId);
const auto viaBotId = UserId(); history->addNewLocalMessage({
history->addNewLocalMessage( .id = newId.msg,
newId.msg, .flags = flags,
flags, .from = messageFromId,
viaBotId, .replyTo = action.replyTo,
message.action.replyTo, .date = HistoryItem::NewMessageDate(action.options),
HistoryItem::NewMessageDate(message.action.options.scheduled), .shortcutId = action.options.shortcutId,
messageFromId, .postAuthor = messagePostAuthor,
messagePostAuthor, }, media, caption);
media,
caption,
HistoryMessageMarkupData());
const auto performRequest = [=](const auto &repeatRequest) -> void { const auto performRequest = [=](const auto &repeatRequest) -> void {
auto &histories = history->owner().histories(); auto &histories = history->owner().histories();
const auto session = &history->session();
const auto usedFileReference = media->fileReference(); const auto usedFileReference = media->fileReference();
histories.sendPreparedMessage( histories.sendPreparedMessage(
history, history,
message.action.replyTo, action.replyTo,
randomId, randomId,
Data::Histories::PrepareMessage<MTPmessages_SendMedia>( Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(sendFlags), MTP_flags(sendFlags),
@ -160,8 +164,9 @@ void SendExistingMedia(
MTP_long(randomId), MTP_long(randomId),
MTPReplyMarkup(), MTPReplyMarkup(),
sentEntities, sentEntities,
MTP_int(message.action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()) (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId)
), [=](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
@ -180,7 +185,7 @@ void SendExistingMedia(
}; };
performRequest(performRequest); performRequest(performRequest);
api->finishForwarding(message.action); api->finishForwarding(action);
} }
} // namespace } // namespace
@ -259,7 +264,10 @@ bool SendDice(MessageToSend &message) {
message.textWithTags = TextWithTags(); message.textWithTags = TextWithTags();
message.action.clearDraft = false; message.action.clearDraft = false;
message.action.generateLocal = true; message.action.generateLocal = true;
api->sendAction(message.action);
const auto &action = message.action;
api->sendAction(action);
const auto newId = FullMsgId( const auto newId = FullMsgId(
peer->id, peer->id,
@ -269,17 +277,17 @@ bool SendDice(MessageToSend &message) {
auto &histories = history->owner().histories(); auto &histories = history->owner().histories();
auto flags = NewMessageFlags(peer); auto flags = NewMessageFlags(peer);
auto sendFlags = MTPmessages_SendMedia::Flags(0); auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (message.action.replyTo) { if (action.replyTo) {
flags |= MessageFlag::HasReplyInfo; flags |= MessageFlag::HasReplyInfo;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to; sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
} }
const auto anonymousPost = peer->amAnonymous(); const auto anonymousPost = peer->amAnonymous();
const auto silentPost = ShouldSendSilent(peer, message.action.options); const auto silentPost = ShouldSendSilent(peer, action.options);
InnerFillMessagePostFlags(message.action.options, peer, flags); InnerFillMessagePostFlags(action.options, peer, flags);
if (silentPost) { if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent; sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
} }
const auto sendAs = message.action.options.sendAs; const auto sendAs = action.options.sendAs;
const auto messageFromId = sendAs const auto messageFromId = sendAs
? sendAs->id ? sendAs->id
: anonymousPost : anonymousPost
@ -292,28 +300,31 @@ bool SendDice(MessageToSend &message) {
? session->user()->name() ? session->user()->name()
: QString(); : QString();
if (message.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;
} }
if (action.options.shortcutId) {
flags |= MessageFlag::ShortcutMessage;
sendFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
}
session->data().registerMessageRandomId(randomId, newId); session->data().registerMessageRandomId(randomId, newId);
const auto viaBotId = UserId(); history->addNewLocalMessage({
history->addNewLocalMessage( .id = newId.msg,
newId.msg, .flags = flags,
flags, .from = messageFromId,
viaBotId, .replyTo = action.replyTo,
message.action.replyTo, .date = HistoryItem::NewMessageDate(action.options),
HistoryItem::NewMessageDate(message.action.options.scheduled), .shortcutId = action.options.shortcutId,
messageFromId, .postAuthor = messagePostAuthor,
messagePostAuthor, }, TextWithEntities(), MTP_messageMediaDice(
TextWithEntities(), MTP_int(0),
MTP_messageMediaDice(MTP_int(0), MTP_string(emoji)), MTP_string(emoji)));
HistoryMessageMarkupData());
histories.sendPreparedMessage( histories.sendPreparedMessage(
history, history,
message.action.replyTo, action.replyTo,
randomId, randomId,
Data::Histories::PrepareMessage<MTPmessages_SendMedia>( Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(sendFlags), MTP_flags(sendFlags),
@ -324,13 +335,14 @@ bool SendDice(MessageToSend &message) {
MTP_long(randomId), MTP_long(randomId),
MTPReplyMarkup(), MTPReplyMarkup(),
MTP_vector<MTPMessageEntity>(), MTP_vector<MTPMessageEntity>(),
MTP_int(message.action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()) (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId)
), [=](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);
}); });
api->finishForwarding(message.action); api->finishForwarding(action);
return true; return true;
} }
@ -406,7 +418,13 @@ void SendConfirmedFile(
if (file->to.options.scheduled) { if (file->to.options.scheduled) {
flags |= MessageFlag::IsOrWasScheduled; flags |= MessageFlag::IsOrWasScheduled;
// Scheduled messages have no the 'edited' badge. // Scheduled messages have no 'edited' badge.
flags |= MessageFlag::HideEdited;
}
if (file->to.options.shortcutId) {
flags |= MessageFlag::ShortcutMessage;
// Shortcut messages have no 'edited' badge.
flags |= MessageFlag::HideEdited; flags |= MessageFlag::HideEdited;
} }
if (file->type == SendMediaType::Audio) { if (file->type == SendMediaType::Audio) {
@ -415,8 +433,7 @@ void SendConfirmedFile(
} }
} }
const auto messageFromId = const auto messageFromId = file->to.options.sendAs
file->to.options.sendAs
? file->to.options.sendAs->id ? file->to.options.sendAs->id
: anonymousPost : anonymousPost
? PeerId() ? PeerId()
@ -486,19 +503,16 @@ void SendConfirmedFile(
edition.savePreviousMedia = true; edition.savePreviousMedia = true;
itemToEdit->applyEdition(std::move(edition)); itemToEdit->applyEdition(std::move(edition));
} else { } else {
const auto viaBotId = UserId(); history->addNewLocalMessage({
history->addNewLocalMessage( .id = newId.msg,
newId.msg, .flags = flags,
flags, .from = messageFromId,
viaBotId, .replyTo = file->to.replyTo,
file->to.replyTo, .date = HistoryItem::NewMessageDate(file->to.options),
HistoryItem::NewMessageDate(file->to.options.scheduled), .shortcutId = file->to.options.shortcutId,
messageFromId, .postAuthor = messagePostAuthor,
messagePostAuthor, .groupedId = groupId,
caption, }, caption, media);
media,
HistoryMessageMarkupData(),
groupId);
} }
if (isEditing) { if (isEditing) {

View file

@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/mtp_instance.h" #include "mtproto/mtp_instance.h"
#include "mtproto/mtproto_config.h" #include "mtproto/mtproto_config.h"
#include "mtproto/mtproto_dc_options.h" #include "mtproto/mtproto_dc_options.h"
#include "data/business/data_shortcut_messages.h"
#include "data/notify/data_notify_settings.h" #include "data/notify/data_notify_settings.h"
#include "data/stickers/data_stickers.h" #include "data/stickers/data_stickers.h"
#include "data/data_saved_messages.h" #include "data/data_saved_messages.h"
@ -1141,7 +1142,8 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
MTPlong(), MTPlong(),
MTPMessageReactions(), MTPMessageReactions(),
MTPVector<MTPRestrictionReason>(), MTPVector<MTPRestrictionReason>(),
MTP_int(d.vttl_period().value_or_empty())), MTP_int(d.vttl_period().value_or_empty()),
MTPint()), // quick_reply_shortcut_id
MessageFlags(), MessageFlags(),
NewMessageType::Unread); NewMessageType::Unread);
} break; } break;
@ -1174,7 +1176,8 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
MTPlong(), MTPlong(),
MTPMessageReactions(), MTPMessageReactions(),
MTPVector<MTPRestrictionReason>(), MTPVector<MTPRestrictionReason>(),
MTP_int(d.vttl_period().value_or_empty())), MTP_int(d.vttl_period().value_or_empty()),
MTPint()), // quick_reply_shortcut_id
MessageFlags(), MessageFlags(),
NewMessageType::Unread); NewMessageType::Unread);
} break; } break;
@ -1565,6 +1568,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
if (const auto local = owner.message(id)) { if (const auto local = owner.message(id)) {
if (local->isScheduled()) { if (local->isScheduled()) {
session().data().scheduledMessages().apply(d, local); session().data().scheduledMessages().apply(d, local);
} else if (local->isBusinessShortcut()) {
session().data().shortcutMessages().apply(d, local);
} else { } else {
const auto existing = session().data().message( const auto existing = session().data().message(
id.peer, id.peer,
@ -1780,6 +1785,31 @@ void Updates::feedUpdate(const MTPUpdate &update) {
session().data().scheduledMessages().apply(d); session().data().scheduledMessages().apply(d);
} break; } break;
case mtpc_updateQuickReplies: {
const auto &d = update.c_updateQuickReplies();
session().data().shortcutMessages().apply(d);
} break;
case mtpc_updateNewQuickReply: {
const auto &d = update.c_updateNewQuickReply();
session().data().shortcutMessages().apply(d);
} break;
case mtpc_updateDeleteQuickReply: {
const auto &d = update.c_updateDeleteQuickReply();
session().data().shortcutMessages().apply(d);
} break;
case mtpc_updateQuickReplyMessage: {
const auto &d = update.c_updateQuickReplyMessage();
session().data().shortcutMessages().apply(d);
} break;
case mtpc_updateDeleteQuickReplyMessages: {
const auto &d = update.c_updateDeleteQuickReplyMessages();
session().data().shortcutMessages().apply(d);
} break;
case mtpc_updateWebPage: { case mtpc_updateWebPage: {
auto &d = update.c_updateWebPage(); auto &d = update.c_updateWebPage();

View file

@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_premium.h" #include "api/api_premium.h"
#include "api/api_user_names.h" #include "api/api_user_names.h"
#include "api/api_websites.h" #include "api/api_websites.h"
#include "data/business/data_shortcut_messages.h"
#include "data/notify/data_notify_settings.h" #include "data/notify/data_notify_settings.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_web_page.h" #include "data/data_web_page.h"
@ -968,6 +969,7 @@ void ApiWrap::requestMoreDialogsIfNeeded() {
} }
} }
requestContacts(); requestContacts();
_session->data().shortcutMessages().preloadShortcuts();
} }
void ApiWrap::updateDialogsOffset( void ApiWrap::updateDialogsOffset(
@ -2480,6 +2482,14 @@ void ApiWrap::refreshFileReference(
request(MTPmessages_GetScheduledMessages( request(MTPmessages_GetScheduledMessages(
item->history()->peer->input, item->history()->peer->input,
MTP_vector<MTPint>(1, MTP_int(realId)))); MTP_vector<MTPint>(1, MTP_int(realId))));
} else if (item->isBusinessShortcut()) {
const auto &shortcuts = _session->data().shortcutMessages();
const auto realId = shortcuts.lookupId(item);
request(MTPmessages_GetQuickReplyMessages(
MTP_flags(MTPmessages_GetQuickReplyMessages::Flag::f_id),
MTP_int(item->shortcutId()),
MTP_vector<MTPint>(1, MTP_int(realId)),
MTP_long(0)));
} else if (const auto channel = item->history()->peer->asChannel()) { } else if (const auto channel = item->history()->peer->asChannel()) {
request(MTPchannels_GetMessages( request(MTPchannels_GetMessages(
channel->inputChannel, channel->inputChannel,
@ -3155,6 +3165,7 @@ void ApiWrap::sharedMediaDone(
if (topicRootId && !topic) { if (topicRootId && !topic) {
return; return;
} }
const auto hasMessages = !parsed.messageIds.empty();
_session->storage().add(Storage::SharedMediaAddSlice( _session->storage().add(Storage::SharedMediaAddSlice(
peer->id, peer->id,
topicRootId, topicRootId,
@ -3163,7 +3174,7 @@ void ApiWrap::sharedMediaDone(
parsed.noSkipRange, parsed.noSkipRange,
parsed.fullCount parsed.fullCount
)); ));
if (type == SharedMediaType::Pinned && !parsed.messageIds.empty()) { if (type == SharedMediaType::Pinned && hasMessages) {
peer->owner().history(peer)->setHasPinnedMessages(true); peer->owner().history(peer)->setHasPinnedMessages(true);
if (topic) { if (topic) {
topic->setHasPinnedMessages(true); topic->setHasPinnedMessages(true);
@ -3172,7 +3183,9 @@ void ApiWrap::sharedMediaDone(
} }
void ApiWrap::sendAction(const SendAction &action) { void ApiWrap::sendAction(const SendAction &action) {
if (!action.options.scheduled && !action.replaceMediaOf) { if (!action.options.scheduled
&& !action.options.shortcutId
&& !action.replaceMediaOf) {
const auto topicRootId = action.replyTo.topicRootId; const auto topicRootId = action.replyTo.topicRootId;
const auto topic = topicRootId const auto topic = topicRootId
? action.history->peer->forumTopicFor(topicRootId) ? action.history->peer->forumTopicFor(topicRootId)
@ -3207,11 +3220,13 @@ void ApiWrap::finishForwarding(const SendAction &action) {
} }
_session->data().sendHistoryChangeNotifications(); _session->data().sendHistoryChangeNotifications();
_session->changes().historyUpdated( if (!action.options.shortcutId) {
history, _session->changes().historyUpdated(
(action.options.scheduled history,
? Data::HistoryUpdate::Flag::ScheduledSent (action.options.scheduled
: Data::HistoryUpdate::Flag::MessageSent)); ? Data::HistoryUpdate::Flag::ScheduledSent
: Data::HistoryUpdate::Flag::MessageSent));
}
} }
void ApiWrap::forwardMessages( void ApiWrap::forwardMessages(
@ -3240,7 +3255,7 @@ void ApiWrap::forwardMessages(
const auto history = action.history; const auto history = action.history;
const auto peer = history->peer; const auto peer = history->peer;
if (!action.options.scheduled) { if (!action.options.scheduled && !action.options.shortcutId) {
histories.readInbox(history); histories.readInbox(history);
} }
const auto anonymousPost = peer->amAnonymous(); const auto anonymousPost = peer->amAnonymous();
@ -3258,6 +3273,10 @@ void ApiWrap::forwardMessages(
flags |= MessageFlag::IsOrWasScheduled; flags |= MessageFlag::IsOrWasScheduled;
sendFlags |= SendFlag::f_schedule_date; sendFlags |= SendFlag::f_schedule_date;
} }
if (action.options.shortcutId) {
flags |= MessageFlag::ShortcutMessage;
sendFlags |= SendFlag::f_quick_reply_shortcut;
}
if (draft.options != Data::ForwardOptions::PreserveInfo) { if (draft.options != Data::ForwardOptions::PreserveInfo) {
sendFlags |= SendFlag::f_drop_author; sendFlags |= SendFlag::f_drop_author;
} }
@ -3296,7 +3315,8 @@ void ApiWrap::forwardMessages(
peer->input, peer->input,
MTP_int(topMsgId), MTP_int(topMsgId),
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)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
applyUpdates(result); applyUpdates(result);
if (shared && !--shared->requestsLeft) { if (shared && !--shared->requestsLeft) {
@ -3340,14 +3360,15 @@ void ApiWrap::forwardMessages(
const auto messagePostAuthor = peer->isBroadcast() const auto messagePostAuthor = peer->isBroadcast()
? self->name() ? self->name()
: QString(); : QString();
history->addNewLocalMessage( history->addNewLocalMessage({
newId.msg, .id = newId.msg,
flags, .flags = flags,
HistoryItem::NewMessageDate(action.options.scheduled), .from = messageFromId,
messageFromId, .replyTo = { .topicRootId = topMsgId },
messagePostAuthor, .date = HistoryItem::NewMessageDate(action.options),
item, .shortcutId = action.options.shortcutId,
topMsgId); .postAuthor = messagePostAuthor,
}, item);
_session->data().registerMessageRandomId(randomId, newId); _session->data().registerMessageRandomId(randomId, newId);
if (!localIds) { if (!localIds) {
localIds = std::make_shared<base::flat_map<uint64, FullMsgId>>(); localIds = std::make_shared<base::flat_map<uint64, FullMsgId>>();
@ -3428,6 +3449,9 @@ void ApiWrap::sendSharedContact(
if (action.options.scheduled) { if (action.options.scheduled) {
flags |= MessageFlag::IsOrWasScheduled; flags |= MessageFlag::IsOrWasScheduled;
} }
if (action.options.shortcutId) {
flags |= MessageFlag::ShortcutMessage;
}
const auto messageFromId = action.options.sendAs const auto messageFromId = action.options.sendAs
? action.options.sendAs->id ? action.options.sendAs->id
: anonymousPost : anonymousPost
@ -3436,23 +3460,20 @@ void ApiWrap::sendSharedContact(
const auto messagePostAuthor = peer->isBroadcast() const auto messagePostAuthor = peer->isBroadcast()
? _session->user()->name() ? _session->user()->name()
: QString(); : QString();
const auto viaBotId = UserId(); const auto item = history->addNewLocalMessage({
const auto item = history->addNewLocalMessage( .id = newId.msg,
newId.msg, .flags = flags,
flags, .from = messageFromId,
viaBotId, .replyTo = action.replyTo,
action.replyTo, .date = HistoryItem::NewMessageDate(action.options),
HistoryItem::NewMessageDate(action.options.scheduled), .shortcutId = action.options.shortcutId,
messageFromId, .postAuthor = messagePostAuthor,
messagePostAuthor, }, TextWithEntities(), MTP_messageMediaContact(
TextWithEntities(), MTP_string(phone),
MTP_messageMediaContact( MTP_string(firstName),
MTP_string(phone), MTP_string(lastName),
MTP_string(firstName), MTP_string(), // vcard
MTP_string(lastName), MTP_long(userId.bare)));
MTP_string(), // vcard
MTP_long(userId.bare)),
HistoryMessageMarkupData());
const auto media = MTP_inputMediaContact( const auto media = MTP_inputMediaContact(
MTP_string(phone), MTP_string(phone),
@ -3635,6 +3656,17 @@ void ApiWrap::cancelLocalItem(not_null<HistoryItem*> item) {
} }
} }
void ApiWrap::sendShortcutMessages(
not_null<PeerData*> peer,
BusinessShortcutId id) {
request(MTPmessages_SendQuickReplyMessages(
peer->input,
MTP_int(id)
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
}).send();
}
void ApiWrap::sendMessage(MessageToSend &&message) { void ApiWrap::sendMessage(MessageToSend &&message) {
const auto history = message.action.history; const auto history = message.action.history;
const auto peer = history->peer; const auto peer = history->peer;
@ -3776,18 +3808,20 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date; sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date;
mediaFlags |= MTPmessages_SendMedia::Flag::f_schedule_date; mediaFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
} }
const auto viaBotId = UserId(); if (action.options.shortcutId) {
lastMessage = history->addNewLocalMessage( flags |= MessageFlag::ShortcutMessage;
newId.msg, sendFlags |= MTPmessages_SendMessage::Flag::f_quick_reply_shortcut;
flags, mediaFlags |= MTPmessages_SendMedia::Flag::f_quick_reply_shortcut;
viaBotId, }
action.replyTo, lastMessage = history->addNewLocalMessage({
HistoryItem::NewMessageDate(action.options.scheduled), .id = newId.msg,
messageFromId, .flags = flags,
messagePostAuthor, .from = messageFromId,
sending, .replyTo = action.replyTo,
media, .date = HistoryItem::NewMessageDate(action.options),
HistoryMessageMarkupData()); .shortcutId = action.options.shortcutId,
.postAuthor = messagePostAuthor,
}, sending, media);
const auto done = [=]( const auto done = [=](
const MTPUpdates &result, const MTPUpdates &result,
const MTP::Response &response) { const MTP::Response &response) {
@ -3813,6 +3847,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
UnixtimeFromMsgId(response.outerMsgId)); UnixtimeFromMsgId(response.outerMsgId));
} }
}; };
const auto mtpShortcut = Data::ShortcutIdToMTP(
_session,
action.options.shortcutId);
if (exactWebPage if (exactWebPage
&& !ignoreWebPage && !ignoreWebPage
&& (manualWebPage || sending.empty())) { && (manualWebPage || sending.empty())) {
@ -3829,8 +3866,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
MTP_long(randomId), MTP_long(randomId),
MTPReplyMarkup(), MTPReplyMarkup(),
sentEntities, sentEntities,
MTP_int(message.action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()) (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
mtpShortcut
), done, fail); ), done, fail);
} else { } else {
histories.sendPreparedMessage( histories.sendPreparedMessage(
@ -3846,7 +3884,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
MTPReplyMarkup(), MTPReplyMarkup(),
sentEntities, sentEntities,
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()) (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
mtpShortcut
), done, fail); ), done, fail);
} }
isFirst = false; isFirst = false;
@ -3937,6 +3976,10 @@ void ApiWrap::sendInlineResult(
flags |= MessageFlag::IsOrWasScheduled; flags |= MessageFlag::IsOrWasScheduled;
sendFlags |= SendFlag::f_schedule_date; sendFlags |= SendFlag::f_schedule_date;
} }
if (action.options.shortcutId) {
flags |= MessageFlag::ShortcutMessage;
sendFlags |= SendFlag::f_quick_reply_shortcut;
}
if (action.options.hideViaBot) { if (action.options.hideViaBot) {
sendFlags |= SendFlag::f_hide_via; sendFlags |= SendFlag::f_hide_via;
} }
@ -3955,15 +3998,18 @@ void ApiWrap::sendInlineResult(
_session->data().registerMessageRandomId(randomId, newId); _session->data().registerMessageRandomId(randomId, newId);
data->addToHistory( data->addToHistory(history, {
history, .id = newId.msg,
flags, .flags = flags,
newId.msg, .from = messageFromId,
messageFromId, .replyTo = action.replyTo,
HistoryItem::NewMessageDate(action.options.scheduled), .date = HistoryItem::NewMessageDate(action.options),
(bot && !action.options.hideViaBot) ? peerToUser(bot->id) : 0, .shortcutId = action.options.shortcutId,
action.replyTo, .viaBotId = ((bot && !action.options.hideViaBot)
messagePostAuthor); ? peerToUser(bot->id)
: UserId()),
.postAuthor = messagePostAuthor,
});
history->clearCloudDraft(topicRootId); history->clearCloudDraft(topicRootId);
history->startSavingCloudDraft(topicRootId); history->startSavingCloudDraft(topicRootId);
@ -3981,7 +4027,8 @@ void ApiWrap::sendInlineResult(
MTP_long(data->getQueryId()), MTP_long(data->getQueryId()),
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)
), [=](const MTPUpdates &result, const MTP::Response &response) { ), [=](const MTPUpdates &result, const MTP::Response &response) {
history->finishSavingCloudDraft( history->finishSavingCloudDraft(
topicRootId, topicRootId,
@ -4118,7 +4165,8 @@ void ApiWrap::sendMediaWithRandomId(
: Flag(0)) : Flag(0))
| (!sentEntities.v.isEmpty() ? Flag::f_entities : Flag(0)) | (!sentEntities.v.isEmpty() ? Flag::f_entities : Flag(0))
| (options.scheduled ? Flag::f_schedule_date : Flag(0)) | (options.scheduled ? Flag::f_schedule_date : Flag(0))
| (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));
auto &histories = history->owner().histories(); auto &histories = history->owner().histories();
const auto peer = history->peer; const auto peer = history->peer;
@ -4137,7 +4185,8 @@ void ApiWrap::sendMediaWithRandomId(
MTPReplyMarkup(), MTPReplyMarkup(),
sentEntities, sentEntities,
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)
), [=](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) {
@ -4233,7 +4282,10 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
? Flag::f_silent ? Flag::f_silent
: Flag(0)) : Flag(0))
| (album->options.scheduled ? Flag::f_schedule_date : Flag(0)) | (album->options.scheduled ? Flag::f_schedule_date : Flag(0))
| (sendAs ? Flag::f_send_as : Flag(0)); | (sendAs ? Flag::f_send_as : Flag(0))
| (album->options.shortcutId
? Flag::f_quick_reply_shortcut
: Flag(0));
auto &histories = history->owner().histories(); auto &histories = history->owner().histories();
const auto peer = history->peer; const auto peer = history->peer;
histories.sendPreparedMessage( histories.sendPreparedMessage(
@ -4246,7 +4298,8 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
Data::Histories::ReplyToPlaceholder(), Data::Histories::ReplyToPlaceholder(),
MTP_vector<MTPInputSingleMedia>(medias), MTP_vector<MTPInputSingleMedia>(medias),
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)
), [=](const MTPUpdates &result, const MTP::Response &response) { ), [=](const MTPUpdates &result, const MTP::Response &response) {
_sendingAlbums.remove(groupId); _sendingAlbums.remove(groupId);

View file

@ -337,6 +337,9 @@ public:
void cancelLocalItem(not_null<HistoryItem*> item); void cancelLocalItem(not_null<HistoryItem*> item);
void sendShortcutMessages(
not_null<PeerData*> peer,
BusinessShortcutId id);
void sendMessage(MessageToSend &&message); void sendMessage(MessageToSend &&message);
void sendBotStart( void sendBotStart(
not_null<UserData*> bot, not_null<UserData*> bot,

View file

@ -1018,7 +1018,7 @@ void SetupChannelBox::prepare() {
cancel); cancel);
connect(_link, &Ui::MaskedInputField::changed, [=] { handleChange(); }); connect(_link, &Ui::MaskedInputField::changed, [=] { handleChange(); });
_link->setVisible(_privacyGroup->value() == Privacy::Public); _link->setVisible(_privacyGroup->current() == Privacy::Public);
_privacyGroup->setChangedCallback([=](Privacy value) { _privacyGroup->setChangedCallback([=](Privacy value) {
privacyChanged(value); privacyChanged(value);
@ -1063,7 +1063,7 @@ void SetupChannelBox::updateMaxHeight() {
: 0) : 0)
+ st::newGroupPadding.bottom(); + st::newGroupPadding.bottom();
if (!_channel->isMegagroup() if (!_channel->isMegagroup()
|| _privacyGroup->value() == Privacy::Public) { || _privacyGroup->current() == Privacy::Public) {
newHeight += st::newGroupLinkPadding.top() newHeight += st::newGroupLinkPadding.top()
+ _link->height() + _link->height()
+ st::newGroupLinkPadding.bottom(); + st::newGroupLinkPadding.bottom();
@ -1264,7 +1264,7 @@ void SetupChannelBox::save() {
}; };
if (_saveRequestId) { if (_saveRequestId) {
return; return;
} else if (_privacyGroup->value() == Privacy::Private) { } else if (_privacyGroup->current() == Privacy::Private) {
closeBox(); closeBox();
} else { } else {
const auto link = _link->text().trimmed(); const auto link = _link->text().trimmed();

View file

@ -81,9 +81,9 @@ void AutoLockBox::prepare() {
const auto timeInput = Ui::CreateChild<Ui::TimeInput>( const auto timeInput = Ui::CreateChild<Ui::TimeInput>(
this, this,
(group->value() == kCustom) (group->current() == kCustom
? TimeString(currentTime) ? TimeString(currentTime)
: kDefaultCustom.utf8(), : kDefaultCustom.utf8()),
st::autolockTimeField, st::autolockTimeField,
st::autolockDateField, st::autolockDateField,
st::scheduleTimeSeparator, st::scheduleTimeSeparator,
@ -115,7 +115,9 @@ void AutoLockBox::prepare() {
}); });
rpl::merge( rpl::merge(
boxClosing() | rpl::filter([=] { return group->value() == kCustom; }), boxClosing() | rpl::filter(
[=] { return group->current() == kCustom; }
),
timeInput->submitRequests() timeInput->submitRequests()
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
if (const auto result = collect()) { if (const auto result = collect()) {

View file

@ -31,8 +31,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_item_helpers.h" #include "history/history_item_helpers.h"
#include "history/view/history_view_message.h" #include "history/view/history_view_message.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "data/data_session.h" #include "data/data_session.h"
@ -42,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document_resolver.h" #include "data/data_document_resolver.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
#include "data/data_premium_limits.h"
#include "settings/settings_premium.h" #include "settings/settings_premium.h"
#include "storage/file_upload.h" #include "storage/file_upload.h"
#include "storage/localimageloader.h" #include "storage/localimageloader.h"
@ -82,11 +81,11 @@ constexpr auto kMaxWallPaperSlugLength = 255;
const auto flags = MessageFlag::FakeHistoryItem const auto flags = MessageFlag::FakeHistoryItem
| MessageFlag::HasFromId | MessageFlag::HasFromId
| (out ? MessageFlag::Outgoing : MessageFlag(0)); | (out ? MessageFlag::Outgoing : MessageFlag(0));
const auto item = history->makeMessage( const auto item = history->makeMessage({
history->owner().nextLocalMessageId(), .id = history->owner().nextLocalMessageId(),
flags, .flags = flags,
base::unixtime::now(), .date = base::unixtime::now(),
PreparedServiceText{ { text } }); }, PreparedServiceText{ { text } });
return AdminLog::OwnedItem(delegate, item); return AdminLog::OwnedItem(delegate, item);
} }
@ -97,24 +96,16 @@ constexpr auto kMaxWallPaperSlugLength = 255;
bool out) { bool out) {
Expects(history->peer->isUser()); Expects(history->peer->isUser());
const auto flags = MessageFlag::FakeHistoryItem const auto item = history->makeMessage({
| MessageFlag::HasFromId .id = history->nextNonHistoryEntryId(),
| (out ? MessageFlag::Outgoing : MessageFlag(0)); .flags = (MessageFlag::FakeHistoryItem
const auto replyTo = FullReplyTo(); | MessageFlag::HasFromId
const auto viaBotId = UserId(); | (out ? MessageFlag::Outgoing : MessageFlag(0))),
const auto groupedId = uint64(); .from = (out
const auto item = history->makeMessage( ? history->session().userId()
history->nextNonHistoryEntryId(), : peerToUser(history->peer->id)),
flags, .date = base::unixtime::now(),
replyTo, }, TextWithEntities{ text }, MTP_messageMediaEmpty());
viaBotId,
base::unixtime::now(),
out ? history->session().userId() : peerToUser(history->peer->id),
QString(),
TextWithEntities{ text },
MTP_messageMediaEmpty(),
HistoryMessageMarkupData(),
groupedId);
return AdminLog::OwnedItem(delegate, item); return AdminLog::OwnedItem(delegate, item);
} }
@ -699,16 +690,10 @@ void BackgroundPreviewBox::checkLevelForChannel() {
if (!weak) { if (!weak) {
return std::optional<Ui::AskBoostReason>(); return std::optional<Ui::AskBoostReason>();
} }
const auto appConfig = &_forPeer->session().account().appConfig(); const auto limits = Data::LevelLimits(&_forPeer->session());
const auto defaultRequired = appConfig->get<int>(
"channel_wallpaper_level_min",
9);
const auto customRequired = appConfig->get<int>(
"channel_custom_wallpaper_level_min",
10);
const auto required = _paperEmojiId.isEmpty() const auto required = _paperEmojiId.isEmpty()
? customRequired ? limits.channelCustomWallpaperLevelMin()
: defaultRequired; : limits.channelWallpaperLevelMin();
if (level >= required) { if (level >= required) {
applyForPeer(false); applyForPeer(false);
return std::optional<Ui::AskBoostReason>(); return std::optional<Ui::AskBoostReason>();
@ -791,7 +776,7 @@ void BackgroundPreviewBox::applyForPeer() {
} else { } else {
ShowPremiumPreviewBox( ShowPremiumPreviewBox(
_controller->uiShow(), _controller->uiShow(),
PremiumPreview::Wallpapers); PremiumFeature::Wallpapers);
} }
}); });
const auto cancel = CreateChild<RoundButton>( const auto cancel = CreateChild<RoundButton>(

View file

@ -717,7 +717,7 @@ void ProxiesBox::refreshProxyForCalls() {
return; return;
} }
_proxyForCalls->toggle( _proxyForCalls->toggle(
(_proxySettings->value() == ProxyData::Settings::Enabled (_proxySettings->current() == ProxyData::Settings::Enabled
&& _currentProxySupportsCallsId != 0), && _currentProxySupportsCallsId != 0),
anim::type::normal); anim::type::normal);
} }
@ -864,7 +864,7 @@ void ProxyBox::refreshButtons() {
addButton(tr::lng_settings_save(), [=] { save(); }); addButton(tr::lng_settings_save(), [=] { save(); });
addButton(tr::lng_cancel(), [=] { closeBox(); }); addButton(tr::lng_cancel(), [=] { closeBox(); });
const auto type = _type->value(); const auto type = _type->current();
if (type == Type::Socks5 || type == Type::Mtproto) { if (type == Type::Socks5 || type == Type::Mtproto) {
addLeftButton(tr::lng_proxy_share(), [=] { share(); }); addLeftButton(tr::lng_proxy_share(), [=] { share(); });
} }
@ -885,7 +885,7 @@ void ProxyBox::share() {
ProxyData ProxyBox::collectData() { ProxyData ProxyBox::collectData() {
auto result = ProxyData(); auto result = ProxyData();
result.type = _type->value(); result.type = _type->current();
result.host = _host->getLastText().trimmed(); result.host = _host->getLastText().trimmed();
result.port = _port->getLastText().trimmed().toInt(); result.port = _port->getLastText().trimmed().toInt();
result.user = (result.type == Type::Mtproto) result.user = (result.type == Type::Mtproto)
@ -1053,7 +1053,7 @@ void ProxyBox::setupControls(const ProxyData &data) {
handleType(type); handleType(type);
refreshButtons(); refreshButtons();
}); });
handleType(_type->value()); handleType(_type->current());
} }
void ProxyBox::addLabel( void ProxyBox::addLabel(

View file

@ -44,7 +44,9 @@ void DownloadPathBox::prepare() {
setTitle(tr::lng_download_path_header()); setTitle(tr::lng_download_path_header());
_group->setChangedCallback([this](Directory value) { radioChanged(value); }); _group->setChangedCallback([this](Directory value) {
radioChanged(value);
});
_pathLink->addClickHandler([=] { editPath(); }); _pathLink->addClickHandler([=] { editPath(); });
if (!_path.isEmpty() && _path != FileDialog::Tmp()) { if (!_path.isEmpty() && _path != FileDialog::Tmp()) {
@ -54,7 +56,7 @@ void DownloadPathBox::prepare() {
} }
void DownloadPathBox::updateControlsVisibility() { void DownloadPathBox::updateControlsVisibility() {
auto custom = (_group->value() == Directory::Custom); auto custom = (_group->current() == Directory::Custom);
_pathLink->setVisible(custom); _pathLink->setVisible(custom);
auto newHeight = st::boxOptionListPadding.top() + (_default ? _default->getMargins().top() + _default->heightNoMargins() : 0) + st::boxOptionListSkip + _temp->heightNoMargins() + st::boxOptionListSkip + _dir->heightNoMargins(); auto newHeight = st::boxOptionListPadding.top() + (_default ? _default->getMargins().top() + _default->heightNoMargins() : 0) + st::boxOptionListSkip + _temp->heightNoMargins() + st::boxOptionListSkip + _dir->heightNoMargins();
@ -122,7 +124,7 @@ void DownloadPathBox::editPath() {
void DownloadPathBox::save() { void DownloadPathBox::save() {
#ifndef OS_WIN_STORE #ifndef OS_WIN_STORE
auto value = _group->value(); auto value = _group->current();
auto computePath = [this, value] { auto computePath = [this, value] {
if (value == Directory::Custom) { if (value == Directory::Custom) {
return _path; return _path;

View file

@ -682,7 +682,7 @@ void EditCaptionBox::setupEmojiPanel() {
&& !_controller->session().premium()) { && !_controller->session().premium()) {
ShowPremiumPreviewBox( ShowPremiumPreviewBox(
_controller, _controller,
PremiumPreview::AnimatedEmoji); PremiumFeature::AnimatedEmoji);
} else { } else {
Data::InsertCustomEmoji(_field.get(), data.document); Data::InsertCustomEmoji(_field.get(), data.document);
} }
@ -895,6 +895,7 @@ void EditCaptionBox::save() {
auto options = Api::SendOptions(); auto options = Api::SendOptions();
options.scheduled = item->isScheduled() ? item->date() : 0; options.scheduled = item->isScheduled() ? item->date() : 0;
options.shortcutId = item->shortcutId();
if (!_preparedList.files.empty()) { if (!_preparedList.files.empty()) {
if ((_albumType != Ui::AlbumType::None) if ((_albumType != Ui::AlbumType::None)

View file

@ -10,28 +10,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_global_privacy.h" #include "api/api_global_privacy.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/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.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"
#include "ui/wrap/vertical_layout.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/vertical_list.h" #include "ui/vertical_list.h"
#include "history/history.h" #include "history/history.h"
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "settings/settings_common.h"
#include "settings/settings_premium.h" #include "settings/settings_premium.h"
#include "settings/settings_privacy_security.h" #include "settings/settings_privacy_security.h"
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "base/binary_guard.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_peer_values.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "styles/style_settings.h" #include "styles/style_settings.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
@ -67,6 +63,32 @@ void CreateRadiobuttonLock(
}, lock->lifetime()); }, lock->lifetime());
} }
void AddPremiumRequiredRow(
not_null<Ui::RpWidget*> widget,
not_null<Main::Session*> session,
Fn<void()> clickedCallback,
Fn<void()> setDefaultOption,
const style::Checkbox &st) {
const auto row = Ui::CreateChild<Ui::AbstractButton>(widget.get());
widget->sizeValue(
) | rpl::start_with_next([=](const QSize &s) {
row->resize(s);
}, row->lifetime());
row->setClickedCallback(std::move(clickedCallback));
CreateRadiobuttonLock(row, st);
Data::AmPremiumValue(
session
) | rpl::start_with_next([=](bool premium) {
row->setVisible(!premium);
if (!premium) {
setDefaultOption();
}
}, row->lifetime());
}
} // namespace } // namespace
class PrivacyExceptionsBoxController : public ChatsListBoxController { class PrivacyExceptionsBoxController : public ChatsListBoxController {
@ -363,10 +385,29 @@ void EditPrivacyBox::setupContent() {
content, content,
_controller->optionsTitleKey(), _controller->optionsTitleKey(),
{ 0, st::settingsPrivacySkipTop, 0, 0 }); { 0, st::settingsPrivacySkipTop, 0, 0 });
addOptionRow(Option::Everyone);
addOptionRow(Option::Contacts); const auto options = {
addOptionRow(Option::CloseFriends); Option::Everyone,
addOptionRow(Option::Nobody); Option::Contacts,
Option::CloseFriends,
Option::Nobody,
};
for (const auto &option : options) {
if (const auto row = addOptionRow(option)) {
const auto premiumCallback = _controller->premiumClickedCallback(
option,
_window);
if (premiumCallback) {
AddPremiumRequiredRow(
row,
&_window->session(),
premiumCallback,
[=] { group->setValue(Option::Everyone); },
st::messagePrivacyCheck);
}
}
}
const auto warning = addLabelOrDivider( const auto warning = addLabelOrDivider(
content, content,
_controller->warning(), _controller->warning(),
@ -541,7 +582,7 @@ void EditMessagesPrivacyBox(
box->addButton(tr::lng_settings_save(), [=] { box->addButton(tr::lng_settings_save(), [=] {
if (controller->session().premium()) { if (controller->session().premium()) {
privacy->updateNewRequirePremium( privacy->updateNewRequirePremium(
group->value() == kOptionPremium); group->current() == kOptionPremium);
box->closeBox(); box->closeBox();
} else { } else {
showToast(); showToast();

View file

@ -88,6 +88,12 @@ public:
virtual void saveAdditional() { virtual void saveAdditional() {
} }
[[nodiscard]] virtual Fn<void()> premiumClickedCallback(
Option option,
not_null<Window::SessionController*> controller) {
return nullptr;
}
virtual ~EditPrivacyController() = default; virtual ~EditPrivacyController() = default;
protected: protected:

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/filters/edit_filter_box.h" #include "boxes/filters/edit_filter_box.h"
#include "boxes/filters/edit_filter_chats_list.h" #include "boxes/filters/edit_filter_chats_list.h"
#include "boxes/filters/edit_filter_chats_preview.h"
#include "boxes/filters/edit_filter_links.h" #include "boxes/filters/edit_filter_links.h"
#include "boxes/premium_limits_box.h" #include "boxes/premium_limits_box.h"
#include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/emoji_suggestions_widget.h"
@ -26,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_peer_values.h" // Data::AmPremiumValue. #include "data/data_peer_values.h" // Data::AmPremiumValue.
#include "data/data_premium_limits.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "core/application.h" #include "core/application.h"
@ -56,60 +58,6 @@ using Flags = Data::ChatFilter::Flags;
using ExceptionPeersRef = const base::flat_set<not_null<History*>> &; using ExceptionPeersRef = const base::flat_set<not_null<History*>> &;
using ExceptionPeersGetter = ExceptionPeersRef(Data::ChatFilter::*)() const; using ExceptionPeersGetter = ExceptionPeersRef(Data::ChatFilter::*)() const;
constexpr auto kAllTypes = {
Flag::Contacts,
Flag::NonContacts,
Flag::Groups,
Flag::Channels,
Flag::Bots,
Flag::NoMuted,
Flag::NoRead,
Flag::NoArchived,
};
class FilterChatsPreview final : public Ui::RpWidget {
public:
FilterChatsPreview(
not_null<QWidget*> parent,
Flags flags,
const base::flat_set<not_null<History*>> &peers);
[[nodiscard]] rpl::producer<Flag> flagRemoved() const;
[[nodiscard]] rpl::producer<not_null<History*>> peerRemoved() const;
void updateData(
Flags flags,
const base::flat_set<not_null<History*>> &peers);
int resizeGetHeight(int newWidth) override;
private:
using Button = base::unique_qptr<Ui::IconButton>;
struct FlagButton {
Flag flag = Flag();
Button button;
};
struct PeerButton {
not_null<History*> history;
Ui::PeerUserpicView userpic;
Ui::Text::String name;
Button button;
};
void paintEvent(QPaintEvent *e) override;
void refresh();
void removeFlag(Flag flag);
void removePeer(not_null<History*> history);
std::vector<FlagButton> _removeFlag;
std::vector<PeerButton> _removePeer;
rpl::event_stream<Flag> _flagRemoved;
rpl::event_stream<not_null<History*>> _peerRemoved;
};
struct NameEditing { struct NameEditing {
not_null<Ui::InputField*> field; not_null<Ui::InputField*> field;
bool custom = false; bool custom = false;
@ -167,167 +115,6 @@ not_null<FilterChatsPreview*> SetupChatsPreview(
return preview; return preview;
} }
FilterChatsPreview::FilterChatsPreview(
not_null<QWidget*> parent,
Flags flags,
const base::flat_set<not_null<History*>> &peers)
: RpWidget(parent) {
updateData(flags, peers);
}
void FilterChatsPreview::refresh() {
resizeToWidth(width());
}
void FilterChatsPreview::updateData(
Flags flags,
const base::flat_set<not_null<History*>> &peers) {
_removeFlag.clear();
_removePeer.clear();
const auto makeButton = [&](Fn<void()> handler) {
auto result = base::make_unique_q<Ui::IconButton>(
this,
st::windowFilterSmallRemove);
result->setClickedCallback(std::move(handler));
return result;
};
for (const auto flag : kAllTypes) {
if (flags & flag) {
_removeFlag.push_back({
flag,
makeButton([=] { removeFlag(flag); }) });
}
}
for (const auto &history : peers) {
_removePeer.push_back(PeerButton{
.history = history,
.button = makeButton([=] { removePeer(history); })
});
}
refresh();
}
int FilterChatsPreview::resizeGetHeight(int newWidth) {
const auto right = st::windowFilterSmallRemoveRight;
const auto add = (st::windowFilterSmallItem.height
- st::windowFilterSmallRemove.height) / 2;
auto top = 0;
const auto moveNextButton = [&](not_null<Ui::IconButton*> button) {
button->moveToRight(right, top + add, newWidth);
top += st::windowFilterSmallItem.height;
};
for (const auto &[flag, button] : _removeFlag) {
moveNextButton(button.get());
}
for (const auto &[history, userpic, name, button] : _removePeer) {
moveNextButton(button.get());
}
return top;
}
void FilterChatsPreview::paintEvent(QPaintEvent *e) {
auto p = Painter(this);
auto top = 0;
const auto &st = st::windowFilterSmallItem;
const auto iconLeft = st.photoPosition.x();
const auto iconTop = st.photoPosition.y();
const auto nameLeft = st.namePosition.x();
p.setFont(st::windowFilterSmallItem.nameStyle.font);
const auto nameTop = st.namePosition.y();
for (const auto &[flag, button] : _removeFlag) {
PaintFilterChatsTypeIcon(
p,
flag,
iconLeft,
top + iconTop,
width(),
st.photoSize);
p.setPen(st::contactsNameFg);
p.drawTextLeft(
nameLeft,
top + nameTop,
width(),
FilterChatsTypeName(flag));
top += st.height;
}
for (auto &[history, userpic, name, button] : _removePeer) {
const auto savedMessages = history->peer->isSelf();
const auto repliesMessages = history->peer->isRepliesChat();
if (savedMessages || repliesMessages) {
if (savedMessages) {
Ui::EmptyUserpic::PaintSavedMessages(
p,
iconLeft,
top + iconTop,
width(),
st.photoSize);
} else {
Ui::EmptyUserpic::PaintRepliesMessages(
p,
iconLeft,
top + iconTop,
width(),
st.photoSize);
}
p.setPen(st::contactsNameFg);
p.drawTextLeft(
nameLeft,
top + nameTop,
width(),
(savedMessages
? tr::lng_saved_messages(tr::now)
: tr::lng_replies_messages(tr::now)));
} else {
history->peer->paintUserpicLeft(
p,
userpic,
iconLeft,
top + iconTop,
width(),
st.photoSize);
p.setPen(st::contactsNameFg);
if (name.isEmpty()) {
name.setText(
st::msgNameStyle,
history->peer->name(),
Ui::NameTextOptions());
}
name.drawLeftElided(
p,
nameLeft,
top + nameTop,
button->x() - nameLeft,
width());
}
top += st.height;
}
}
void FilterChatsPreview::removeFlag(Flag flag) {
const auto i = ranges::find(_removeFlag, flag, &FlagButton::flag);
Assert(i != end(_removeFlag));
_removeFlag.erase(i);
refresh();
_flagRemoved.fire_copy(flag);
}
void FilterChatsPreview::removePeer(not_null<History*> history) {
const auto i = ranges::find(_removePeer, history, &PeerButton::history);
Assert(i != end(_removePeer));
_removePeer.erase(i);
refresh();
_peerRemoved.fire_copy(history);
}
rpl::producer<Flag> FilterChatsPreview::flagRemoved() const {
return _flagRemoved.events();
}
rpl::producer<not_null<History*>> FilterChatsPreview::peerRemoved() const {
return _peerRemoved.events();
}
void EditExceptions( void EditExceptions(
not_null<Window::SessionController*> window, not_null<Window::SessionController*> window,
not_null<QObject*> context, not_null<QObject*> context,
@ -338,6 +125,12 @@ void EditExceptions(
const auto include = (options & Flag::Contacts) != Flags(0); const auto include = (options & Flag::Contacts) != Flags(0);
const auto rules = data->current(); const auto rules = data->current();
const auto session = &window->session(); const auto session = &window->session();
const auto limit = Data::PremiumLimits(
session
).dialogFiltersChatsCurrent();
const auto showLimitReached = [=] {
window->show(Box(FilterChatsLimitBox, session, limit, include));
};
auto controller = std::make_unique<EditFilterChatsListController>( auto controller = std::make_unique<EditFilterChatsListController>(
session, session,
(include (include
@ -346,9 +139,8 @@ void EditExceptions(
options, options,
rules.flags() & options, rules.flags() & options,
include ? rules.always() : rules.never(), include ? rules.always() : rules.never(),
[=](int count) { limit,
return Box(FilterChatsLimitBox, session, count, include); showLimitReached);
});
const auto rawController = controller.get(); const auto rawController = controller.get();
auto initBox = [=](not_null<PeerListBox*> box) { auto initBox = [=](not_null<PeerListBox*> box) {
box->setCloseByOutsideClick(false); box->setCloseByOutsideClick(false);

View file

@ -28,6 +28,8 @@ using Flag = Data::ChatFilter::Flag;
using Flags = Data::ChatFilter::Flags; using Flags = Data::ChatFilter::Flags;
constexpr auto kAllTypes = { constexpr auto kAllTypes = {
Flag::NewChats,
Flag::ExistingChats,
Flag::Contacts, Flag::Contacts,
Flag::NonContacts, Flag::NonContacts,
Flag::Groups, Flag::Groups,
@ -119,7 +121,7 @@ PaintRoundImageCallback TypeRow::generatePaintUserpicCallback(
} }
Flag TypeRow::flag() const { Flag TypeRow::flag() const {
return static_cast<Flag>(id() & 0xFF); return static_cast<Flag>(id() & 0xFFFF);
} }
ExceptionRow::ExceptionRow(not_null<History*> history) : Row(history) { ExceptionRow::ExceptionRow(not_null<History*> history) : Row(history) {
@ -219,6 +221,8 @@ auto TypeController::rowSelectionChanges() const
[[nodiscard]] QString FilterChatsTypeName(Flag flag) { [[nodiscard]] QString FilterChatsTypeName(Flag flag) {
switch (flag) { switch (flag) {
case Flag::NewChats: return tr::lng_filters_type_new(tr::now);
case Flag::ExistingChats: return tr::lng_filters_type_existing(tr::now);
case Flag::Contacts: return tr::lng_filters_type_contacts(tr::now); case Flag::Contacts: return tr::lng_filters_type_contacts(tr::now);
case Flag::NonContacts: case Flag::NonContacts:
return tr::lng_filters_type_non_contacts(tr::now); return tr::lng_filters_type_non_contacts(tr::now);
@ -241,6 +245,8 @@ void PaintFilterChatsTypeIcon(
int size) { int size) {
const auto &color1 = [&]() -> const style::color& { const auto &color1 = [&]() -> const style::color& {
switch (flag) { switch (flag) {
case Flag::NewChats: return st::historyPeer5UserpicBg;
case Flag::ExistingChats: return st::historyPeer8UserpicBg;
case Flag::Contacts: return st::historyPeer4UserpicBg; case Flag::Contacts: return st::historyPeer4UserpicBg;
case Flag::NonContacts: return st::historyPeer7UserpicBg; case Flag::NonContacts: return st::historyPeer7UserpicBg;
case Flag::Groups: return st::historyPeer2UserpicBg; case Flag::Groups: return st::historyPeer2UserpicBg;
@ -254,6 +260,8 @@ void PaintFilterChatsTypeIcon(
}(); }();
const auto &color2 = [&]() -> const style::color& { const auto &color2 = [&]() -> const style::color& {
switch (flag) { switch (flag) {
case Flag::NewChats: return st::historyPeer5UserpicBg2;
case Flag::ExistingChats: return st::historyPeer8UserpicBg2;
case Flag::Contacts: return st::historyPeer4UserpicBg2; case Flag::Contacts: return st::historyPeer4UserpicBg2;
case Flag::NonContacts: return st::historyPeer7UserpicBg2; case Flag::NonContacts: return st::historyPeer7UserpicBg2;
case Flag::Groups: return st::historyPeer2UserpicBg2; case Flag::Groups: return st::historyPeer2UserpicBg2;
@ -267,6 +275,8 @@ void PaintFilterChatsTypeIcon(
}(); }();
const auto &icon = [&]() -> const style::icon& { const auto &icon = [&]() -> const style::icon& {
switch (flag) { switch (flag) {
case Flag::NewChats: return st::windowFilterTypeNewChats;
case Flag::ExistingChats: return st::windowFilterTypeExistingChats;
case Flag::Contacts: return st::windowFilterTypeContacts; case Flag::Contacts: return st::windowFilterTypeContacts;
case Flag::NonContacts: return st::windowFilterTypeNonContacts; case Flag::NonContacts: return st::windowFilterTypeNonContacts;
case Flag::Groups: return st::windowFilterTypeGroups; case Flag::Groups: return st::windowFilterTypeGroups;
@ -323,17 +333,17 @@ EditFilterChatsListController::EditFilterChatsListController(
Flags options, Flags options,
Flags selected, Flags selected,
const base::flat_set<not_null<History*>> &peers, const base::flat_set<not_null<History*>> &peers,
LimitBoxFactory limitBox) int limit,
Fn<void()> showLimitReached)
: ChatsListBoxController(session) : ChatsListBoxController(session)
, _session(session) , _session(session)
, _limitBox(std::move(limitBox)) , _showLimitReached(std::move(showLimitReached))
, _title(std::move(title)) , _title(std::move(title))
, _peers(peers) , _peers(peers)
, _options(options & ~Flag::Chatlist) , _options(options & ~Flag::Chatlist)
, _selected(selected) , _selected(selected)
, _limit(Data::PremiumLimits(session).dialogFiltersChatsCurrent()) , _limit(limit)
, _chatlist(options & Flag::Chatlist) { , _chatlist(options & Flag::Chatlist) {
Expects(_limitBox != nullptr);
} }
Main::Session &EditFilterChatsListController::session() const { Main::Session &EditFilterChatsListController::session() const {
@ -361,8 +371,8 @@ void EditFilterChatsListController::rowClicked(not_null<PeerListRow*> row) {
if (count < _limit || row->checked()) { if (count < _limit || row->checked()) {
delegate()->peerListSetRowChecked(row, !row->checked()); delegate()->peerListSetRowChecked(row, !row->checked());
updateTitle(); updateTitle();
} else { } else if (const auto copy = _showLimitReached) {
delegate()->peerListUiShow()->showBox(_limitBox(count)); copy();
} }
} }
@ -469,6 +479,10 @@ object_ptr<Ui::RpWidget> EditFilterChatsListController::prepareTypesList() {
auto EditFilterChatsListController::createRow(not_null<History*> history) auto EditFilterChatsListController::createRow(not_null<History*> history)
-> std::unique_ptr<Row> { -> std::unique_ptr<Row> {
const auto business = _options & (Flag::NewChats | Flag::ExistingChats);
if (business && (history->peer->isSelf() || !history->peer->isUser())) {
return nullptr;
}
return history->inChatList() return history->inChatList()
? std::make_unique<ExceptionRow>(history) ? std::make_unique<ExceptionRow>(history)
: nullptr; : nullptr;

View file

@ -43,7 +43,6 @@ class EditFilterChatsListController final : public ChatsListBoxController {
public: public:
using Flag = Data::ChatFilter::Flag; using Flag = Data::ChatFilter::Flag;
using Flags = Data::ChatFilter::Flags; using Flags = Data::ChatFilter::Flags;
using LimitBoxFactory = Fn<object_ptr<Ui::BoxContent>(int)>;
EditFilterChatsListController( EditFilterChatsListController(
not_null<Main::Session*> session, not_null<Main::Session*> session,
@ -51,7 +50,8 @@ public:
Flags options, Flags options,
Flags selected, Flags selected,
const base::flat_set<not_null<History*>> &peers, const base::flat_set<not_null<History*>> &peers,
LimitBoxFactory limitBox); int limit,
Fn<void()> showLimitReached);
[[nodiscard]] Main::Session &session() const override; [[nodiscard]] Main::Session &session() const override;
[[nodiscard]] Flags chosenOptions() const { [[nodiscard]] Flags chosenOptions() const {
@ -72,7 +72,7 @@ private:
void updateTitle(); void updateTitle();
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
const LimitBoxFactory _limitBox; const Fn<void()> _showLimitReached;
rpl::producer<QString> _title; rpl::producer<QString> _title;
base::flat_set<not_null<History*>> _peers; base::flat_set<not_null<History*>> _peers;
Flags _options; Flags _options;

View file

@ -0,0 +1,199 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/filters/edit_filter_chats_preview.h"
#include "boxes/filters/edit_filter_chats_list.h"
#include "data/data_peer.h"
#include "history/history.h"
#include "lang/lang_keys.h"
#include "ui/text/text_options.h"
#include "ui/widgets/buttons.h"
#include "ui/painter.h"
#include "styles/style_chat.h"
#include "styles/style_window.h"
namespace {
using Flag = Data::ChatFilter::Flag;
constexpr auto kAllTypes = {
Flag::NewChats,
Flag::ExistingChats,
Flag::Contacts,
Flag::NonContacts,
Flag::Groups,
Flag::Channels,
Flag::Bots,
Flag::NoMuted,
Flag::NoRead,
Flag::NoArchived,
};
} // namespace
FilterChatsPreview::FilterChatsPreview(
not_null<QWidget*> parent,
Flags flags,
const base::flat_set<not_null<History*>> &peers)
: RpWidget(parent) {
updateData(flags, peers);
}
void FilterChatsPreview::refresh() {
resizeToWidth(width());
}
void FilterChatsPreview::updateData(
Flags flags,
const base::flat_set<not_null<History*>> &peers) {
_removeFlag.clear();
_removePeer.clear();
const auto makeButton = [&](Fn<void()> handler) {
auto result = base::make_unique_q<Ui::IconButton>(
this,
st::windowFilterSmallRemove);
result->setClickedCallback(std::move(handler));
result->show();
return result;
};
for (const auto flag : kAllTypes) {
if (flags & flag) {
_removeFlag.push_back({
flag,
makeButton([=] { removeFlag(flag); }) });
}
}
for (const auto &history : peers) {
_removePeer.push_back(PeerButton{
.history = history,
.button = makeButton([=] { removePeer(history); })
});
}
refresh();
}
int FilterChatsPreview::resizeGetHeight(int newWidth) {
const auto right = st::windowFilterSmallRemoveRight;
const auto add = (st::windowFilterSmallItem.height
- st::windowFilterSmallRemove.height) / 2;
auto top = 0;
const auto moveNextButton = [&](not_null<Ui::IconButton*> button) {
button->moveToRight(right, top + add, newWidth);
top += st::windowFilterSmallItem.height;
};
for (const auto &[flag, button] : _removeFlag) {
moveNextButton(button.get());
}
for (const auto &[history, userpic, name, button] : _removePeer) {
moveNextButton(button.get());
}
return top;
}
void FilterChatsPreview::paintEvent(QPaintEvent *e) {
auto p = Painter(this);
auto top = 0;
const auto &st = st::windowFilterSmallItem;
const auto iconLeft = st.photoPosition.x();
const auto iconTop = st.photoPosition.y();
const auto nameLeft = st.namePosition.x();
p.setFont(st::windowFilterSmallItem.nameStyle.font);
const auto nameTop = st.namePosition.y();
for (const auto &[flag, button] : _removeFlag) {
PaintFilterChatsTypeIcon(
p,
flag,
iconLeft,
top + iconTop,
width(),
st.photoSize);
p.setPen(st::contactsNameFg);
p.drawTextLeft(
nameLeft,
top + nameTop,
width(),
FilterChatsTypeName(flag));
top += st.height;
}
for (auto &[history, userpic, name, button] : _removePeer) {
const auto savedMessages = history->peer->isSelf();
const auto repliesMessages = history->peer->isRepliesChat();
if (savedMessages || repliesMessages) {
if (savedMessages) {
Ui::EmptyUserpic::PaintSavedMessages(
p,
iconLeft,
top + iconTop,
width(),
st.photoSize);
} else {
Ui::EmptyUserpic::PaintRepliesMessages(
p,
iconLeft,
top + iconTop,
width(),
st.photoSize);
}
p.setPen(st::contactsNameFg);
p.drawTextLeft(
nameLeft,
top + nameTop,
width(),
(savedMessages
? tr::lng_saved_messages(tr::now)
: tr::lng_replies_messages(tr::now)));
} else {
history->peer->paintUserpicLeft(
p,
userpic,
iconLeft,
top + iconTop,
width(),
st.photoSize);
p.setPen(st::contactsNameFg);
if (name.isEmpty()) {
name.setText(
st::msgNameStyle,
history->peer->name(),
Ui::NameTextOptions());
}
name.drawLeftElided(
p,
nameLeft,
top + nameTop,
button->x() - nameLeft,
width());
}
top += st.height;
}
}
void FilterChatsPreview::removeFlag(Flag flag) {
const auto i = ranges::find(_removeFlag, flag, &FlagButton::flag);
Assert(i != end(_removeFlag));
_removeFlag.erase(i);
refresh();
_flagRemoved.fire_copy(flag);
}
void FilterChatsPreview::removePeer(not_null<History*> history) {
const auto i = ranges::find(_removePeer, history, &PeerButton::history);
Assert(i != end(_removePeer));
_removePeer.erase(i);
refresh();
_peerRemoved.fire_copy(history);
}
rpl::producer<Flag> FilterChatsPreview::flagRemoved() const {
return _flagRemoved.events();
}
rpl::producer<not_null<History*>> FilterChatsPreview::peerRemoved() const {
return _peerRemoved.events();
}

View file

@ -0,0 +1,64 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_chat_filters.h"
#include "ui/rp_widget.h"
#include "ui/userpic_view.h"
class History;
namespace Ui {
class IconButton;
} // namespace Ui
class FilterChatsPreview final : public Ui::RpWidget {
public:
using Flag = Data::ChatFilter::Flag;
using Flags = Data::ChatFilter::Flags;
FilterChatsPreview(
not_null<QWidget*> parent,
Flags flags,
const base::flat_set<not_null<History*>> &peers);
[[nodiscard]] rpl::producer<Flag> flagRemoved() const;
[[nodiscard]] rpl::producer<not_null<History*>> peerRemoved() const;
void updateData(
Flags flags,
const base::flat_set<not_null<History*>> &peers);
int resizeGetHeight(int newWidth) override;
private:
using Button = base::unique_qptr<Ui::IconButton>;
struct FlagButton {
Flag flag = Flag();
Button button;
};
struct PeerButton {
not_null<History*> history;
Ui::PeerUserpicView userpic;
Ui::Text::String name;
Button button;
};
void paintEvent(QPaintEvent *e) override;
void refresh();
void removeFlag(Flag flag);
void removePeer(not_null<History*> history);
std::vector<FlagButton> _removeFlag;
std::vector<PeerButton> _removePeer;
rpl::event_stream<Flag> _flagRemoved;
rpl::event_stream<not_null<History*>> _peerRemoved;
};

View file

@ -982,8 +982,7 @@ bool GoodForExportFilterLink(
not_null<Window::SessionController*> window, not_null<Window::SessionController*> window,
const Data::ChatFilter &filter) { const Data::ChatFilter &filter) {
using Flag = Data::ChatFilter::Flag; using Flag = Data::ChatFilter::Flag;
const auto listflags = Flag::Chatlist | Flag::HasMyLinks; if (!filter.never().empty() || (filter.flags() & Flag::RulesMask)) {
if (!filter.never().empty() || (filter.flags() & ~listflags)) {
window->showToast(tr::lng_filters_link_cant(tr::now)); window->showToast(tr::lng_filters_link_cant(tr::now));
return false; return false;
} }

View file

@ -386,7 +386,7 @@ void GiftBox(
state->buttonText.events(), state->buttonText.events(),
Ui::Premium::GiftGradientStops(), Ui::Premium::GiftGradientStops(),
[=] { [=] {
const auto value = group->value(); const auto value = group->current();
return (value < options.size() && value >= 0) return (value < options.size() && value >= 0)
? options[value].botUrl ? options[value].botUrl
: QString(); : QString();
@ -587,7 +587,7 @@ void GiftsBox(
const auto content = box->addRow( const auto content = box->addRow(
object_ptr<Ui::VerticalLayout>(box), object_ptr<Ui::VerticalLayout>(box),
{}); {});
auto buttonCallback = [=](PremiumPreview section) { auto buttonCallback = [=](PremiumFeature section) {
stars->setPaused(true); stars->setPaused(true);
const auto previewBoxShown = [=]( const auto previewBoxShown = [=](
not_null<Ui::BoxContent*> previewBox) { not_null<Ui::BoxContent*> previewBox) {
@ -665,7 +665,7 @@ void GiftsBox(
} }
auto invoice = api->invoice( auto invoice = api->invoice(
users.size(), users.size(),
api->monthsFromPreset(group->value())); api->monthsFromPreset(group->current()));
invoice.purpose = Payments::InvoicePremiumGiftCodeUsers{ users }; invoice.purpose = Payments::InvoicePremiumGiftCodeUsers{ users };
state->confirmButtonBusy = true; state->confirmButtonBusy = true;

View file

@ -1216,7 +1216,7 @@ void LanguageBox::setupTop(not_null<Ui::VerticalLayout*> container) {
if (checked && !premium) { if (checked && !premium) {
ShowPremiumPreviewToBuy( ShowPremiumPreviewToBuy(
_controller, _controller,
PremiumPreview::RealTimeTranslation); PremiumFeature::RealTimeTranslation);
_translateChatTurnOff.fire(false); _translateChatTurnOff.fire(false);
} }
return premium return premium

View file

@ -260,11 +260,11 @@ void Controller::choose(not_null<ChatData*> chat) {
const auto init = [=](not_null<ListBox*> box) { const auto init = [=](not_null<ListBox*> box) {
auto above = object_ptr<Ui::VerticalLayout>(box); auto above = object_ptr<Ui::VerticalLayout>(box);
Settings::AddDividerTextWithLottie( Settings::AddDividerTextWithLottie(above, {
above, .lottie = u"discussion"_q,
box->showFinishes(), .showFinished = box->showFinishes(),
About(channel, chat), .about = About(channel, chat),
u"discussion"_q); });
if (!chat) { if (!chat) {
Assert(channel->isBroadcast()); Assert(channel->isBroadcast());

View file

@ -216,6 +216,9 @@ ChatAdminRightsInfo EditAdminBox::defaultRights() const {
: peer()->isMegagroup() : peer()->isMegagroup()
? ChatAdminRightsInfo{ (Flag::ChangeInfo ? ChatAdminRightsInfo{ (Flag::ChangeInfo
| Flag::DeleteMessages | Flag::DeleteMessages
| Flag::PostStories
| Flag::EditStories
| Flag::DeleteStories
| Flag::BanUsers | Flag::BanUsers
| Flag::InviteByLinkOrAdd | Flag::InviteByLinkOrAdd
| Flag::ManageTopics | Flag::ManageTopics
@ -225,6 +228,9 @@ ChatAdminRightsInfo EditAdminBox::defaultRights() const {
| Flag::PostMessages | Flag::PostMessages
| Flag::EditMessages | Flag::EditMessages
| Flag::DeleteMessages | Flag::DeleteMessages
| Flag::PostStories
| Flag::EditStories
| Flag::DeleteStories
| Flag::InviteByLinkOrAdd | Flag::InviteByLinkOrAdd
| Flag::ManageCall) }; | Flag::ManageCall) };
} }
@ -840,7 +846,7 @@ void EditRestrictedBox::createUntilGroup() {
void EditRestrictedBox::createUntilVariants() { void EditRestrictedBox::createUntilVariants() {
auto addVariant = [&](int value, const QString &text) { auto addVariant = [&](int value, const QString &text) {
if (!canSave() && _untilGroup->value() != value) { if (!canSave() && _untilGroup->current() != value) {
return; return;
} }
_untilVariants.emplace_back( _untilVariants.emplace_back(

View file

@ -15,6 +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" // 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"
@ -23,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_emoji_statuses.h" #include "data/data_emoji_statuses.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_premium_limits.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_web_page.h" #include "data/data_web_page.h"
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
@ -34,8 +36,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "lottie/lottie_icon.h" #include "lottie/lottie_icon.h"
#include "lottie/lottie_single_player.h" #include "lottie/lottie_single_player.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "settings/settings_common.h" #include "settings/settings_common.h"
#include "settings/settings_premium.h" #include "settings/settings_premium.h"
@ -43,10 +43,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/chat/chat_style.h" #include "ui/chat/chat_style.h"
#include "ui/chat/chat_theme.h" #include "ui/chat/chat_theme.h"
#include "ui/effects/path_shift_gradient.h" #include "ui/effects/path_shift_gradient.h"
#include "ui/effects/premium_graphics.h"
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/rect.h"
#include "ui/vertical_list.h" #include "ui/vertical_list.h"
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
#include "window/section_widget.h" #include "window/section_widget.h"
@ -146,6 +148,28 @@ private:
}; };
class LevelBadge final : public Ui::RpWidget {
public:
LevelBadge(
not_null<QWidget*> parent,
uint32 level,
not_null<Main::Session*> session);
void setMinimal(bool value);
private:
void paintEvent(QPaintEvent *e) override;
void updateText();
const uint32 _level;
const TextWithEntities _icon;
const Core::MarkedTextContext _context;
Ui::Text::String _text;
bool _minimal = false;
};
ColorSample::ColorSample( ColorSample::ColorSample(
not_null<QWidget*> parent, not_null<QWidget*> parent,
std::shared_ptr<Ui::ChatStyle> style, std::shared_ptr<Ui::ChatStyle> style,
@ -295,47 +319,36 @@ PreviewWrap::PreviewWrap(
, _delegate(std::make_unique<PreviewDelegate>(box, _style.get(), [=] { , _delegate(std::make_unique<PreviewDelegate>(box, _style.get(), [=] {
update(); update();
})) }))
, _replyToItem(_history->addNewLocalMessage( , _replyToItem(_history->addNewLocalMessage({
_history->nextNonHistoryEntryId(), .id = _history->nextNonHistoryEntryId(),
(MessageFlag::FakeHistoryItem .flags = (MessageFlag::FakeHistoryItem
| MessageFlag::HasFromId | MessageFlag::HasFromId
| MessageFlag::Post), | MessageFlag::Post),
UserId(), // via .from = _fake->id,
FullReplyTo(), .date = base::unixtime::now(),
base::unixtime::now(), // date }, TextWithEntities{ _peer->isSelf()
_fake->id, ? tr::lng_settings_color_reply(tr::now)
QString(), // postAuthor : tr::lng_settings_color_reply_channel(tr::now),
TextWithEntities{ _peer->isSelf() }, MTP_messageMediaEmpty()))
? tr::lng_settings_color_reply(tr::now) , _replyItem(_history->addNewLocalMessage({
: tr::lng_settings_color_reply_channel(tr::now), .id = _history->nextNonHistoryEntryId(),
}, .flags = (MessageFlag::FakeHistoryItem
MTP_messageMediaEmpty(),
HistoryMessageMarkupData(),
uint64(0)))
, _replyItem(_history->addNewLocalMessage(
_history->nextNonHistoryEntryId(),
(MessageFlag::FakeHistoryItem
| MessageFlag::HasFromId | MessageFlag::HasFromId
| MessageFlag::HasReplyInfo | MessageFlag::HasReplyInfo
| MessageFlag::Post), | MessageFlag::Post),
UserId(), // via .from = _fake->id,
FullReplyTo{ .messageId = _replyToItem->fullId() }, .replyTo = FullReplyTo{.messageId = _replyToItem->fullId() },
base::unixtime::now(), // date .date = base::unixtime::now(),
_fake->id, }, TextWithEntities{ _peer->isSelf()
QString(), // postAuthor ? tr::lng_settings_color_text(tr::now)
TextWithEntities{ _peer->isSelf() : tr::lng_settings_color_text_channel(tr::now),
? tr::lng_settings_color_text(tr::now) }, MTP_messageMediaWebPage(
: tr::lng_settings_color_text_channel(tr::now), MTP_flags(0),
}, MTP_webPagePending(
MTP_messageMediaWebPage(
MTP_flags(0), MTP_flags(0),
MTP_webPagePending( MTP_long(_webpage->id),
MTP_flags(0), MTPstring(),
MTP_long(_webpage->id), MTP_int(0)))))
MTPstring(),
MTP_int(0))),
HistoryMessageMarkupData(),
uint64(0)))
, _element(_replyItem->createView(_delegate.get())) , _element(_replyItem->createView(_delegate.get()))
, _position(0, st::msgMargin.bottom()) { , _position(0, st::msgMargin.bottom()) {
_style->apply(_theme.get()); _style->apply(_theme.get());
@ -437,6 +450,108 @@ HistoryView::Context PreviewDelegate::elementContext() {
return HistoryView::Context::AdminLog; return HistoryView::Context::AdminLog;
} }
LevelBadge::LevelBadge(
not_null<QWidget*> parent,
uint32 level,
not_null<Main::Session*> session)
: Ui::RpWidget(parent)
, _level(level)
, _icon(Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::settingsLevelBadgeLock,
QMargins(0, st::settingsLevelBadgeLockSkip, 0, 0),
false)))
, _context({ .session = session }) {
updateText();
}
void LevelBadge::updateText() {
auto text = _icon;
text.append(' ');
if (!_minimal) {
text.append(tr::lng_boost_level(
tr::now,
lt_count,
_level,
Ui::Text::WithEntities));
} else {
text.append(QString::number(_level));
}
const auto &st = st::settingsPremiumNewBadge.style;
_text.setMarkedText(
st,
text,
kMarkupTextOptions,
_context);
const auto &padding = st::settingsColorSamplePadding;
QWidget::resize(
_text.maxWidth() + rect::m::sum::h(padding),
st.font->height + rect::m::sum::v(padding));
}
void LevelBadge::setMinimal(bool value) {
if ((value != _minimal) && value) {
_minimal = value;
updateText();
update();
}
}
void LevelBadge::paintEvent(QPaintEvent *e) {
auto p = QPainter(this);
auto hq = PainterHighQualityEnabler(p);
const auto radius = height() / 2;
p.setPen(Qt::NoPen);
auto gradient = QLinearGradient(QPointF(0, 0), QPointF(width(), 0));
gradient.setStops(Ui::Premium::ButtonGradientStops());
p.setBrush(gradient);
p.drawRoundedRect(rect(), radius, radius);
p.setPen(st::premiumButtonFg);
p.setBrush(Qt::NoBrush);
const auto context = Ui::Text::PaintContext{
.position = rect::m::pos::tl(st::settingsColorSamplePadding),
.outerWidth = width(),
.availableWidth = width(),
};
_text.draw(p, context);
}
void AddLevelBadge(
int level,
not_null<Ui::SettingsButton*> button,
Ui::RpWidget *right,
not_null<ChannelData*> channel,
const QMargins &padding,
rpl::producer<QString> text) {
if (channel->levelHint() >= level) {
return;
}
const auto badge = Ui::CreateChild<LevelBadge>(
button.get(),
level,
&channel->session());
badge->show();
const auto sampleLeft = st::settingsColorSamplePadding.left();
const auto badgeLeft = padding.left() + sampleLeft;
rpl::combine(
button->sizeValue(),
std::move(text)
) | rpl::start_with_next([=](const QSize &s, const QString &) {
if (s.isNull()) {
return;
}
badge->moveToLeft(
button->fullTextWidth() + badgeLeft,
(s.height() - badge->height()) / 2);
const auto rightEdge = right ? right->pos().x() : button->width();
badge->setMinimal((rect::right(badge) + sampleLeft) > rightEdge);
badge->setVisible((rect::right(badge) + sampleLeft) < rightEdge);
}, badge->lifetime());
}
struct SetValues { struct SetValues {
uint8 colorIndex = 0; uint8 colorIndex = 0;
DocumentId backgroundEmojiId = 0; DocumentId backgroundEmojiId = 0;
@ -541,16 +656,13 @@ void Apply(
: peerColors->requiredChannelLevelFor( : peerColors->requiredChannelLevelFor(
peer->id, peer->id,
values.colorIndex); values.colorIndex);
const auto limits = Data::LevelLimits(&peer->session());
const auto iconRequired = values.backgroundEmojiId const auto iconRequired = values.backgroundEmojiId
? session->account().appConfig().get<int>( ? limits.channelBgIconLevelMin()
"channel_bg_icon_level_min",
5)
: 0; : 0;
const auto statusRequired = (values.statusChanged const auto statusRequired = (values.statusChanged
&& values.statusId) && values.statusId)
? session->account().appConfig().get<int>( ? limits.channelEmojiStatusLevelMin()
"channel_emoji_status_level_min",
8)
: 0; : 0;
const auto required = std::max({ const auto required = std::max({
colorRequired, colorRequired,
@ -726,6 +838,7 @@ struct ButtonWithEmoji {
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<ChatHelpers::Show> show,
std::shared_ptr<Ui::ChatStyle> style, std::shared_ptr<Ui::ChatStyle> style,
not_null<PeerData*> peer,
rpl::producer<uint8> colorIndexValue, rpl::producer<uint8> colorIndexValue,
rpl::producer<DocumentId> emojiIdValue, rpl::producer<DocumentId> emojiIdValue,
Fn<void(DocumentId)> emojiIdChosen) { Fn<void(DocumentId)> emojiIdChosen) {
@ -827,21 +940,33 @@ struct ButtonWithEmoji {
} }
}); });
if (const auto channel = peer->asChannel()) {
AddLevelBadge(
Data::LevelLimits(&channel->session()).channelBgIconLevelMin(),
raw,
right,
channel,
button.st->padding,
tr::lng_settings_color_emoji());
}
return result; return result;
} }
[[nodiscard]] object_ptr<Ui::SettingsButton> CreateEmojiStatusButton( [[nodiscard]] object_ptr<Ui::SettingsButton> CreateEmojiStatusButton(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<ChatHelpers::Show> show,
not_null<ChannelData*> channel,
rpl::producer<DocumentId> statusIdValue, rpl::producer<DocumentId> statusIdValue,
Fn<void(DocumentId,TimeId)> statusIdChosen, Fn<void(DocumentId,TimeId)> statusIdChosen,
bool group) { bool group) {
const auto button = ButtonStyleWithRightEmoji(parent); const auto button = ButtonStyleWithRightEmoji(parent);
const auto &phrase = group
? tr::lng_edit_channel_status_group
: tr::lng_edit_channel_status;
auto result = Settings::CreateButtonWithIcon( auto result = Settings::CreateButtonWithIcon(
parent, parent,
(group phrase(),
? tr::lng_edit_channel_status_group()
: tr::lng_edit_channel_status()),
*button.st, *button.st,
{ &st::menuBlueIconEmojiStatus }); { &st::menuBlueIconEmojiStatus });
const auto raw = result.data(); const auto raw = result.data();
@ -926,6 +1051,17 @@ struct ButtonWithEmoji {
} }
}); });
const auto limits = Data::LevelLimits(&channel->session());
AddLevelBadge(
(group
? limits.groupEmojiStatusLevelMin()
: limits.channelEmojiStatusLevelMin()),
raw,
right,
channel,
button.st->padding,
phrase());
return result; return result;
} }
@ -1036,6 +1172,14 @@ struct ButtonWithEmoji {
} }
}, right->lifetime()); }, right->lifetime());
AddLevelBadge(
Data::LevelLimits(&channel->session()).groupEmojiStickersLevelMin(),
raw,
right,
channel,
button.st->padding,
tr::lng_group_emoji());
return result; return result;
} }
@ -1068,38 +1212,15 @@ void EditPeerColorBox(
state->index = peer->colorIndex(); state->index = peer->colorIndex();
state->emojiId = peer->backgroundEmojiId(); state->emojiId = peer->backgroundEmojiId();
state->statusId = peer->emojiStatusId(); state->statusId = peer->emojiStatusId();
if (group) { if (group) {
const auto divider = Ui::CreateChild<Ui::BoxContentDivider>( Settings::AddDividerTextWithLottie(box->verticalLayout(), {
box.get()); .lottie = u"palette"_q,
const auto verticalLayout = box->verticalLayout()->add( .lottieSize = st::settingsCloudPasswordIconSize,
object_ptr<Ui::VerticalLayout>(box.get())); .lottieMargins = st::peerAppearanceIconPadding,
.showFinished = box->showFinishes(),
auto icon = CreateLottieIcon( .about = tr::lng_boost_group_about(Ui::Text::WithEntities),
verticalLayout, .aboutMargins = st::peerAppearanceCoverLabelMargin,
{
.name = u"palette"_q,
.sizeOverride = {
st::settingsCloudPasswordIconSize,
st::settingsCloudPasswordIconSize,
},
},
st::peerAppearanceIconPadding);
box->setShowFinishedCallback([animate = std::move(icon.animate)] {
animate(anim::repeat::once);
}); });
verticalLayout->add(std::move(icon.widget));
verticalLayout->add(
object_ptr<Ui::FlatLabel>(
verticalLayout,
tr::lng_boost_group_about(),
st::peerAppearanceCoverLabel),
st::peerAppearanceCoverLabelMargin);
verticalLayout->geometryValue(
) | rpl::start_with_next([=](const QRect &r) {
divider->setGeometry(r);
}, divider->lifetime());
} else { } else {
box->addRow(object_ptr<PreviewWrap>( box->addRow(object_ptr<PreviewWrap>(
box, box,
@ -1135,6 +1256,7 @@ void EditPeerColorBox(
container, container,
show, show,
style, style,
peer,
state->index.value(), state->index.value(),
state->emojiId.value(), state->emojiId.value(),
[=](DocumentId id) { state->emojiId = id; })); [=](DocumentId id) { state->emojiId = id; }));
@ -1150,20 +1272,35 @@ void EditPeerColorBox(
if (const auto channel = peer->asChannel()) { if (const auto channel = peer->asChannel()) {
Ui::AddSkip(container, st::settingsColorSampleSkip); Ui::AddSkip(container, st::settingsColorSampleSkip);
Settings::AddButtonWithIcon( const auto &phrase = group
? tr::lng_edit_channel_wallpaper_group
: tr::lng_edit_channel_wallpaper;
const auto button = Settings::AddButtonWithIcon(
container, container,
(group phrase(),
? tr::lng_edit_channel_wallpaper_group()
: tr::lng_edit_channel_wallpaper()),
st::peerAppearanceButton, st::peerAppearanceButton,
{ &st::menuBlueIconWallpaper } { &st::menuBlueIconWallpaper }
)->setClickedCallback([=] { );
button->setClickedCallback([=] {
const auto usage = ChatHelpers::WindowUsage::PremiumPromo; const auto usage = ChatHelpers::WindowUsage::PremiumPromo;
if (const auto strong = show->resolveWindow(usage)) { if (const auto strong = show->resolveWindow(usage)) {
show->show(Box<BackgroundBox>(strong, channel)); show->show(Box<BackgroundBox>(strong, channel));
} }
}); });
{
const auto limits = Data::LevelLimits(&channel->session());
AddLevelBadge(
group
? limits.groupCustomWallpaperLevelMin()
: limits.channelCustomWallpaperLevelMin(),
button,
nullptr,
channel,
st::peerAppearanceButton.padding,
phrase());
}
Ui::AddSkip(container, st::settingsColorSampleSkip); Ui::AddSkip(container, st::settingsColorSampleSkip);
Ui::AddDividerText( Ui::AddDividerText(
container, container,
@ -1201,6 +1338,7 @@ void EditPeerColorBox(
container->add(CreateEmojiStatusButton( container->add(CreateEmojiStatusButton(
container, container,
show, show,
channel,
state->statusId.value(), state->statusId.value(),
[=](DocumentId id, TimeId until) { [=](DocumentId id, TimeId until) {
state->statusId = id; state->statusId = id;

View file

@ -25,7 +25,7 @@ void EditPeerHistoryVisibilityBox(
box->setTitle(tr::lng_manage_history_visibility_title()); box->setTitle(tr::lng_manage_history_visibility_title());
box->addButton(tr::lng_settings_save(), [=] { box->addButton(tr::lng_settings_save(), [=] {
savedCallback(historyVisibility->value()); savedCallback(historyVisibility->current());
box->closeBox(); box->closeBox();
}); });
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });

View file

@ -76,7 +76,7 @@ public:
} }
[[nodiscard]] Privacy getPrivacy() const { [[nodiscard]] Privacy getPrivacy() const {
return _controls.privacy->value(); return _controls.privacy->current();
} }
[[nodiscard]] bool noForwards() const { [[nodiscard]] bool noForwards() const {
@ -238,7 +238,7 @@ void Controller::createContent() {
}, wrap->lifetime()); }, wrap->lifetime());
} else { } else {
_controls.whoSendWrap->toggle( _controls.whoSendWrap->toggle(
(_controls.privacy->value() == Privacy::HasUsername), (_controls.privacy->current() == Privacy::HasUsername),
anim::type::instant); anim::type::instant);
} }
auto joinToWrite = _controls.joinToWrite auto joinToWrite = _controls.joinToWrite
@ -299,7 +299,7 @@ void Controller::createContent() {
if (_linkOnly) { if (_linkOnly) {
_controls.inviteLinkWrap->show(anim::type::instant); _controls.inviteLinkWrap->show(anim::type::instant);
} else { } else {
if (_controls.privacy->value() == Privacy::NoUsername) { if (_controls.privacy->current() == Privacy::NoUsername) {
checkUsernameAvailability(); checkUsernameAvailability();
} }
const auto forShowing = _dataSavedValue const auto forShowing = _dataSavedValue
@ -474,7 +474,7 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
&Ui::UsernameInput::changed, &Ui::UsernameInput::changed,
[this] { usernameChanged(); }); [this] { usernameChanged(); });
const auto shown = (_controls.privacy->value() == Privacy::HasUsername); const auto shown = (_controls.privacy->current() == Privacy::HasUsername);
result->toggle(shown, anim::type::instant); result->toggle(shown, anim::type::instant);
return result; return result;
@ -539,7 +539,7 @@ void Controller::checkUsernameAvailability() {
if (!_controls.usernameInput) { if (!_controls.usernameInput) {
return; return;
} }
const auto initial = (_controls.privacy->value() != Privacy::HasUsername); const auto initial = (_controls.privacy->current() != Privacy::HasUsername);
const auto checking = initial const auto checking = initial
? u".bad."_q ? u".bad."_q
: getUsernameInput(); : getUsernameInput();
@ -573,11 +573,11 @@ void Controller::checkUsernameAvailability() {
_controls.privacy->setValue(Privacy::NoUsername); _controls.privacy->setValue(Privacy::NoUsername);
} else if (type == u"CHANNELS_ADMIN_PUBLIC_TOO_MUCH"_q) { } else if (type == u"CHANNELS_ADMIN_PUBLIC_TOO_MUCH"_q) {
_usernameState = UsernameState::TooMany; _usernameState = UsernameState::TooMany;
if (_controls.privacy->value() == Privacy::HasUsername) { if (_controls.privacy->current() == Privacy::HasUsername) {
askUsernameRevoke(); askUsernameRevoke();
} }
} else if (initial) { } else if (initial) {
if (_controls.privacy->value() == Privacy::HasUsername) { if (_controls.privacy->current() == Privacy::HasUsername) {
showUsernameEmpty(); showUsernameEmpty();
setFocusUsername(); setFocusUsername();
} }

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" #include "apiwrap.h"
#include "base/event_filter.h" #include "base/event_filter.h"
#include "base/unixtime.h" #include "base/unixtime.h"
#include "data/data_premium_limits.h"
#include "boxes/peer_list_box.h" #include "boxes/peer_list_box.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_cloud_themes.h" #include "data/data_cloud_themes.h"
@ -424,14 +425,9 @@ Ui::BoostCounters ParseBoostCounters(
} }
Ui::BoostFeatures LookupBoostFeatures(not_null<ChannelData*> channel) { Ui::BoostFeatures LookupBoostFeatures(not_null<ChannelData*> channel) {
const auto group = channel->isMegagroup();
const auto appConfig = &channel->session().account().appConfig();
const auto get = [&](const QString &key, int fallback, bool ok = true) {
return ok ? appConfig->get<int>(key, fallback) : 0;
};
auto nameColorsByLevel = base::flat_map<int, int>(); auto nameColorsByLevel = base::flat_map<int, int>();
auto linkStylesByLevel = base::flat_map<int, int>(); auto linkStylesByLevel = base::flat_map<int, int>();
const auto group = channel->isMegagroup();
const auto peerColors = &channel->session().api().peerColors(); const auto peerColors = &channel->session().api().peerColors();
const auto &list = group const auto &list = group
? peerColors->requiredLevelsGroup() ? peerColors->requiredLevelsGroup()
@ -447,22 +443,23 @@ Ui::BoostFeatures LookupBoostFeatures(not_null<ChannelData*> channel) {
if (themes.empty()) { if (themes.empty()) {
channel->owner().cloudThemes().refreshChatThemes(); channel->owner().cloudThemes().refreshChatThemes();
} }
const auto levelLimits = Data::LevelLimits(&channel->session());
return Ui::BoostFeatures{ return Ui::BoostFeatures{
.nameColorsByLevel = std::move(nameColorsByLevel), .nameColorsByLevel = std::move(nameColorsByLevel),
.linkStylesByLevel = std::move(linkStylesByLevel), .linkStylesByLevel = std::move(linkStylesByLevel),
.linkLogoLevel = get(u"channel_bg_icon_level_min"_q, 4, !group), .linkLogoLevel = group ? 0 : levelLimits.channelBgIconLevelMin(),
.transcribeLevel = get(u"group_transcribe_level_min"_q, 6, group), .transcribeLevel = group ? levelLimits.groupTranscribeLevelMin() : 0,
.emojiPackLevel = get(u"group_emoji_stickers_level_min"_q, 4, group), .emojiPackLevel = group ? levelLimits.groupEmojiStickersLevelMin() : 0,
.emojiStatusLevel = get(group .emojiStatusLevel = group
? u"group_emoji_status_level_min"_q ? levelLimits.groupEmojiStatusLevelMin()
: u"channel_emoji_status_level_min"_q, 8), : levelLimits.channelEmojiStatusLevelMin(),
.wallpaperLevel = get(group .wallpaperLevel = group
? u"group_wallpaper_level_min"_q ? levelLimits.groupWallpaperLevelMin()
: u"channel_wallpaper_level_min"_q, 9), : levelLimits.channelWallpaperLevelMin(),
.wallpapersCount = themes.empty() ? 8 : int(themes.size()), .wallpapersCount = themes.empty() ? 8 : int(themes.size()),
.customWallpaperLevel = get(group .customWallpaperLevel = group
? u"channel_custom_wallpaper_level_min"_q ? levelLimits.groupCustomWallpaperLevelMin()
: u"group_custom_wallpaper_level_min"_q, 10), : levelLimits.channelCustomWallpaperLevelMin(),
}; };
} }

View file

@ -1142,7 +1142,7 @@ void AccountsLimitBox(
const auto ref = QString(); const auto ref = QString();
const auto wasAccount = &session->account(); const auto wasAccount = &session->account();
const auto nowAccount = accounts[group->value()]; const auto nowAccount = accounts[group->current()];
if (wasAccount == nowAccount) { if (wasAccount == nowAccount) {
Settings::ShowPremium(session, ref); Settings::ShowPremium(session, ref);
return; return;

View file

@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/vertical_list.h" #include "ui/vertical_list.h"
#include "settings/settings_business.h"
#include "settings/settings_premium.h" #include "settings/settings_premium.h"
#include "lottie/lottie_single_player.h" #include "lottie/lottie_single_player.h"
#include "history/view/media/history_view_sticker.h" #include "history/view/media/history_view_sticker.h"
@ -59,7 +60,7 @@ constexpr auto kStarPeriod = 3 * crl::time(1000);
using Data::ReactionId; using Data::ReactionId;
struct Descriptor { struct Descriptor {
PremiumPreview section = PremiumPreview::Stickers; PremiumFeature section = PremiumFeature::Stickers;
DocumentData *requestedSticker = nullptr; DocumentData *requestedSticker = nullptr;
bool fromSettings = false; bool fromSettings = false;
Fn<void()> hiddenCallback; Fn<void()> hiddenCallback;
@ -90,88 +91,118 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
media->videoThumbnailWanted(origin); media->videoThumbnailWanted(origin);
} }
[[nodiscard]] rpl::producer<QString> SectionTitle(PremiumPreview section) { [[nodiscard]] rpl::producer<QString> SectionTitle(PremiumFeature section) {
switch (section) { switch (section) {
case PremiumPreview::Wallpapers: case PremiumFeature::Wallpapers:
return tr::lng_premium_summary_subtitle_wallpapers(); return tr::lng_premium_summary_subtitle_wallpapers();
case PremiumPreview::Stories: case PremiumFeature::Stories:
return tr::lng_premium_summary_subtitle_stories(); return tr::lng_premium_summary_subtitle_stories();
case PremiumPreview::DoubleLimits: case PremiumFeature::DoubleLimits:
return tr::lng_premium_summary_subtitle_double_limits(); return tr::lng_premium_summary_subtitle_double_limits();
case PremiumPreview::MoreUpload: case PremiumFeature::MoreUpload:
return tr::lng_premium_summary_subtitle_more_upload(); return tr::lng_premium_summary_subtitle_more_upload();
case PremiumPreview::FasterDownload: case PremiumFeature::FasterDownload:
return tr::lng_premium_summary_subtitle_faster_download(); return tr::lng_premium_summary_subtitle_faster_download();
case PremiumPreview::VoiceToText: case PremiumFeature::VoiceToText:
return tr::lng_premium_summary_subtitle_voice_to_text(); return tr::lng_premium_summary_subtitle_voice_to_text();
case PremiumPreview::NoAds: case PremiumFeature::NoAds:
return tr::lng_premium_summary_subtitle_no_ads(); return tr::lng_premium_summary_subtitle_no_ads();
case PremiumPreview::EmojiStatus: case PremiumFeature::EmojiStatus:
return tr::lng_premium_summary_subtitle_emoji_status(); return tr::lng_premium_summary_subtitle_emoji_status();
case PremiumPreview::InfiniteReactions: case PremiumFeature::InfiniteReactions:
return tr::lng_premium_summary_subtitle_infinite_reactions(); return tr::lng_premium_summary_subtitle_infinite_reactions();
case PremiumPreview::TagsForMessages: case PremiumFeature::TagsForMessages:
return tr::lng_premium_summary_subtitle_tags_for_messages(); return tr::lng_premium_summary_subtitle_tags_for_messages();
case PremiumPreview::LastSeen: case PremiumFeature::LastSeen:
return tr::lng_premium_summary_subtitle_last_seen(); return tr::lng_premium_summary_subtitle_last_seen();
case PremiumPreview::MessagePrivacy: case PremiumFeature::MessagePrivacy:
return tr::lng_premium_summary_subtitle_message_privacy(); return tr::lng_premium_summary_subtitle_message_privacy();
case PremiumPreview::Stickers: case PremiumFeature::Stickers:
return tr::lng_premium_summary_subtitle_premium_stickers(); return tr::lng_premium_summary_subtitle_premium_stickers();
case PremiumPreview::AnimatedEmoji: case PremiumFeature::AnimatedEmoji:
return tr::lng_premium_summary_subtitle_animated_emoji(); return tr::lng_premium_summary_subtitle_animated_emoji();
case PremiumPreview::AdvancedChatManagement: case PremiumFeature::AdvancedChatManagement:
return tr::lng_premium_summary_subtitle_advanced_chat_management(); return tr::lng_premium_summary_subtitle_advanced_chat_management();
case PremiumPreview::ProfileBadge: case PremiumFeature::ProfileBadge:
return tr::lng_premium_summary_subtitle_profile_badge(); return tr::lng_premium_summary_subtitle_profile_badge();
case PremiumPreview::AnimatedUserpics: case PremiumFeature::AnimatedUserpics:
return tr::lng_premium_summary_subtitle_animated_userpics(); return tr::lng_premium_summary_subtitle_animated_userpics();
case PremiumPreview::RealTimeTranslation: case PremiumFeature::RealTimeTranslation:
return tr::lng_premium_summary_subtitle_translation(); return tr::lng_premium_summary_subtitle_translation();
case PremiumFeature::Business:
return tr::lng_premium_summary_subtitle_business();
case PremiumFeature::BusinessLocation:
return tr::lng_business_subtitle_location();
case PremiumFeature::BusinessHours:
return tr::lng_business_subtitle_opening_hours();
case PremiumFeature::QuickReplies:
return tr::lng_business_subtitle_quick_replies();
case PremiumFeature::GreetingMessage:
return tr::lng_business_subtitle_greeting_messages();
case PremiumFeature::AwayMessage:
return tr::lng_business_subtitle_away_messages();
case PremiumFeature::BusinessBots:
return tr::lng_business_subtitle_chatbots();
} }
Unexpected("PremiumPreview in SectionTitle."); Unexpected("PremiumFeature in SectionTitle.");
} }
[[nodiscard]] rpl::producer<QString> SectionAbout(PremiumPreview section) { [[nodiscard]] rpl::producer<QString> SectionAbout(PremiumFeature section) {
switch (section) { switch (section) {
case PremiumPreview::Wallpapers: case PremiumFeature::Wallpapers:
return tr::lng_premium_summary_about_wallpapers(); return tr::lng_premium_summary_about_wallpapers();
case PremiumPreview::Stories: case PremiumFeature::Stories:
return tr::lng_premium_summary_about_stories(); return tr::lng_premium_summary_about_stories();
case PremiumPreview::DoubleLimits: case PremiumFeature::DoubleLimits:
return tr::lng_premium_summary_about_double_limits(); return tr::lng_premium_summary_about_double_limits();
case PremiumPreview::MoreUpload: case PremiumFeature::MoreUpload:
return tr::lng_premium_summary_about_more_upload(); return tr::lng_premium_summary_about_more_upload();
case PremiumPreview::FasterDownload: case PremiumFeature::FasterDownload:
return tr::lng_premium_summary_about_faster_download(); return tr::lng_premium_summary_about_faster_download();
case PremiumPreview::VoiceToText: case PremiumFeature::VoiceToText:
return tr::lng_premium_summary_about_voice_to_text(); return tr::lng_premium_summary_about_voice_to_text();
case PremiumPreview::NoAds: case PremiumFeature::NoAds:
return tr::lng_premium_summary_about_no_ads(); return tr::lng_premium_summary_about_no_ads();
case PremiumPreview::EmojiStatus: case PremiumFeature::EmojiStatus:
return tr::lng_premium_summary_about_emoji_status(); return tr::lng_premium_summary_about_emoji_status();
case PremiumPreview::InfiniteReactions: case PremiumFeature::InfiniteReactions:
return tr::lng_premium_summary_about_infinite_reactions(); return tr::lng_premium_summary_about_infinite_reactions();
case PremiumPreview::TagsForMessages: case PremiumFeature::TagsForMessages:
return tr::lng_premium_summary_about_tags_for_messages(); return tr::lng_premium_summary_about_tags_for_messages();
case PremiumPreview::LastSeen: case PremiumFeature::LastSeen:
return tr::lng_premium_summary_about_last_seen(); return tr::lng_premium_summary_about_last_seen();
case PremiumPreview::MessagePrivacy: case PremiumFeature::MessagePrivacy:
return tr::lng_premium_summary_about_message_privacy(); return tr::lng_premium_summary_about_message_privacy();
case PremiumPreview::Stickers: case PremiumFeature::Stickers:
return tr::lng_premium_summary_about_premium_stickers(); return tr::lng_premium_summary_about_premium_stickers();
case PremiumPreview::AnimatedEmoji: case PremiumFeature::AnimatedEmoji:
return tr::lng_premium_summary_about_animated_emoji(); return tr::lng_premium_summary_about_animated_emoji();
case PremiumPreview::AdvancedChatManagement: case PremiumFeature::AdvancedChatManagement:
return tr::lng_premium_summary_about_advanced_chat_management(); return tr::lng_premium_summary_about_advanced_chat_management();
case PremiumPreview::ProfileBadge: case PremiumFeature::ProfileBadge:
return tr::lng_premium_summary_about_profile_badge(); return tr::lng_premium_summary_about_profile_badge();
case PremiumPreview::AnimatedUserpics: case PremiumFeature::AnimatedUserpics:
return tr::lng_premium_summary_about_animated_userpics(); return tr::lng_premium_summary_about_animated_userpics();
case PremiumPreview::RealTimeTranslation: case PremiumFeature::RealTimeTranslation:
return tr::lng_premium_summary_about_translation(); return tr::lng_premium_summary_about_translation();
case PremiumFeature::Business:
return tr::lng_premium_summary_about_business();
case PremiumFeature::BusinessLocation:
return tr::lng_business_about_location();
case PremiumFeature::BusinessHours:
return tr::lng_business_about_opening_hours();
case PremiumFeature::QuickReplies:
return tr::lng_business_about_quick_replies();
case PremiumFeature::GreetingMessage:
return tr::lng_business_about_greeting_messages();
case PremiumFeature::AwayMessage:
return tr::lng_business_about_away_messages();
case PremiumFeature::BusinessBots:
return tr::lng_business_about_chatbots();
} }
Unexpected("PremiumPreview in SectionTitle."); Unexpected("PremiumFeature in SectionTitle.");
} }
[[nodiscard]] object_ptr<Ui::RpWidget> ChatBackPreview( [[nodiscard]] object_ptr<Ui::RpWidget> ChatBackPreview(
@ -463,33 +494,40 @@ struct VideoPreviewDocument {
RectPart align = RectPart::Bottom; RectPart align = RectPart::Bottom;
}; };
[[nodiscard]] bool VideoAlignToTop(PremiumPreview section) { [[nodiscard]] bool VideoAlignToTop(PremiumFeature section) {
return (section == PremiumPreview::MoreUpload) return (section == PremiumFeature::MoreUpload)
|| (section == PremiumPreview::NoAds) || (section == PremiumFeature::NoAds)
|| (section == PremiumPreview::AnimatedEmoji); || (section == PremiumFeature::AnimatedEmoji);
} }
[[nodiscard]] DocumentData *LookupVideo( [[nodiscard]] DocumentData *LookupVideo(
not_null<Main::Session*> session, not_null<Main::Session*> session,
PremiumPreview section) { PremiumFeature section) {
const auto name = [&] { const auto name = [&] {
switch (section) { switch (section) {
case PremiumPreview::MoreUpload: return "more_upload"; case PremiumFeature::MoreUpload: return "more_upload";
case PremiumPreview::FasterDownload: return "faster_download"; case PremiumFeature::FasterDownload: return "faster_download";
case PremiumPreview::VoiceToText: return "voice_to_text"; case PremiumFeature::VoiceToText: return "voice_to_text";
case PremiumPreview::NoAds: return "no_ads"; case PremiumFeature::NoAds: return "no_ads";
case PremiumPreview::AnimatedEmoji: return "animated_emoji"; case PremiumFeature::AnimatedEmoji: return "animated_emoji";
case PremiumPreview::AdvancedChatManagement: case PremiumFeature::AdvancedChatManagement:
return "advanced_chat_management"; return "advanced_chat_management";
case PremiumPreview::EmojiStatus: return "emoji_status"; case PremiumFeature::EmojiStatus: return "emoji_status";
case PremiumPreview::InfiniteReactions: return "infinite_reactions"; case PremiumFeature::InfiniteReactions: return "infinite_reactions";
case PremiumPreview::TagsForMessages: return "saved_tags"; case PremiumFeature::TagsForMessages: return "saved_tags";
case PremiumPreview::ProfileBadge: return "profile_badge"; case PremiumFeature::ProfileBadge: return "profile_badge";
case PremiumPreview::AnimatedUserpics: return "animated_userpics"; case PremiumFeature::AnimatedUserpics: return "animated_userpics";
case PremiumPreview::RealTimeTranslation: return "translations"; case PremiumFeature::RealTimeTranslation: return "translations";
case PremiumPreview::Wallpapers: return "wallpapers"; case PremiumFeature::Wallpapers: return "wallpapers";
case PremiumPreview::LastSeen: return "last_seen"; case PremiumFeature::LastSeen: return "last_seen";
case PremiumPreview::MessagePrivacy: return "message_privacy"; case PremiumFeature::MessagePrivacy: return "message_privacy";
case PremiumFeature::BusinessLocation: return "business_location";
case PremiumFeature::BusinessHours: return "business_hours";
case PremiumFeature::QuickReplies: return "quick_replies";
case PremiumFeature::GreetingMessage: return "greeting_message";
case PremiumFeature::AwayMessage: return "away_message";
case PremiumFeature::BusinessBots: return "business_bots";
} }
return ""; return "";
}(); }();
@ -716,7 +754,7 @@ struct VideoPreviewDocument {
[[nodiscard]] not_null<Ui::RpWidget*> GenericPreview( [[nodiscard]] not_null<Ui::RpWidget*> GenericPreview(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<ChatHelpers::Show> show,
PremiumPreview section, PremiumFeature section,
Fn<void()> readyCallback) { Fn<void()> readyCallback) {
const auto result = Ui::CreateChild<Ui::RpWidget>(parent.get()); const auto result = Ui::CreateChild<Ui::RpWidget>(parent.get());
result->show(); result->show();
@ -757,10 +795,10 @@ struct VideoPreviewDocument {
[[nodiscard]] not_null<Ui::RpWidget*> GenerateDefaultPreview( [[nodiscard]] not_null<Ui::RpWidget*> GenerateDefaultPreview(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<ChatHelpers::Show> show,
PremiumPreview section, PremiumFeature section,
Fn<void()> readyCallback) { Fn<void()> readyCallback) {
switch (section) { switch (section) {
case PremiumPreview::Stickers: case PremiumFeature::Stickers:
return StickersPreview(parent, std::move(show), readyCallback); return StickersPreview(parent, std::move(show), readyCallback);
default: default:
return GenericPreview( return GenericPreview(
@ -784,8 +822,8 @@ struct VideoPreviewDocument {
[[nodiscard]] object_ptr<Ui::RpWidget> CreateSwitch( [[nodiscard]] object_ptr<Ui::RpWidget> CreateSwitch(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
not_null<rpl::variable<PremiumPreview>*> selected, not_null<rpl::variable<PremiumFeature>*> selected,
std::vector<PremiumPreview> order) { std::vector<PremiumFeature> order) {
const auto padding = st::premiumDotPadding; const auto padding = st::premiumDotPadding;
const auto width = padding.left() + st::premiumDot + padding.right(); const auto width = padding.left() + st::premiumDot + padding.right();
const auto height = padding.top() + st::premiumDot + padding.bottom(); const auto height = padding.top() + st::premiumDot + padding.bottom();
@ -856,14 +894,20 @@ void PreviewBox(
Ui::Animations::Simple animation; Ui::Animations::Simple animation;
Fn<void()> preload; Fn<void()> preload;
std::vector<Hiding> hiding; std::vector<Hiding> hiding;
rpl::variable<PremiumPreview> selected; rpl::variable<PremiumFeature> selected;
std::vector<PremiumPreview> order; std::vector<PremiumFeature> order;
}; };
const auto state = outer->lifetime().make_state<State>(); const auto state = outer->lifetime().make_state<State>();
state->selected = descriptor.section; state->selected = descriptor.section;
state->order = Settings::PremiumPreviewOrder(&show->session()); auto premiumOrder = Settings::PremiumFeaturesOrder(&show->session());
auto businessOrder = Settings::BusinessFeaturesOrder(&show->session());
state->order = ranges::contains(businessOrder, descriptor.section)
? std::move(businessOrder)
: ranges::contains(businessOrder, descriptor.section)
? std::move(premiumOrder)
: std::vector{ descriptor.section };
const auto index = [=](PremiumPreview section) { const auto index = [=](PremiumFeature section) {
const auto it = ranges::find(state->order, section); const auto it = ranges::find(state->order, section);
return (it == end(state->order)) return (it == end(state->order))
? 0 ? 0
@ -906,7 +950,7 @@ void PreviewBox(
return; return;
} }
const auto now = state->selected.current(); const auto now = state->selected.current();
if (now != PremiumPreview::Stickers && !state->stickersPreload) { if (now != PremiumFeature::Stickers && !state->stickersPreload) {
const auto ready = [=] { const auto ready = [=] {
if (state->stickersPreload) { if (state->stickersPreload) {
state->stickersPreloadReady = true; state->stickersPreloadReady = true;
@ -917,14 +961,14 @@ void PreviewBox(
state->stickersPreload = GenerateDefaultPreview( state->stickersPreload = GenerateDefaultPreview(
outer, outer,
show, show,
PremiumPreview::Stickers, PremiumFeature::Stickers,
ready); ready);
state->stickersPreload->hide(); state->stickersPreload->hide();
} }
}; };
switch (descriptor.section) { switch (descriptor.section) {
case PremiumPreview::Stickers: case PremiumFeature::Stickers:
state->content = media state->content = media
? StickerPreview(outer, show, media, state->preload) ? StickerPreview(outer, show, media, state->preload)
: StickersPreview(outer, show, state->preload); : StickersPreview(outer, show, state->preload);
@ -940,7 +984,7 @@ void PreviewBox(
state->selected.value( state->selected.value(
) | rpl::combine_previous( ) | rpl::combine_previous(
) | rpl::start_with_next([=](PremiumPreview was, PremiumPreview now) { ) | rpl::start_with_next([=](PremiumFeature was, PremiumFeature now) {
const auto animationCallback = [=] { const auto animationCallback = [=] {
if (!state->animation.animating()) { if (!state->animation.animating()) {
for (const auto &hiding : base::take(state->hiding)) { for (const auto &hiding : base::take(state->hiding)) {
@ -982,7 +1026,7 @@ void PreviewBox(
.leftTill = state->content->x() - start, .leftTill = state->content->x() - start,
}); });
state->leftFrom = start; state->leftFrom = start;
if (now == PremiumPreview::Stickers && state->stickersPreload) { if (now == PremiumFeature::Stickers && state->stickersPreload) {
state->content = base::take(state->stickersPreload); state->content = base::take(state->stickersPreload);
state->content->show(); state->content->show();
if (base::take(state->stickersPreloadReady)) { if (base::take(state->stickersPreloadReady)) {
@ -1053,14 +1097,14 @@ void PreviewBox(
return Settings::LookupPremiumRef(state->selected.current()); return Settings::LookupPremiumRef(state->selected.current());
}; };
auto unlock = state->selected.value( auto unlock = state->selected.value(
) | rpl::map([=](PremiumPreview section) { ) | rpl::map([=](PremiumFeature section) {
return (section == PremiumPreview::InfiniteReactions) return (section == PremiumFeature::InfiniteReactions)
? tr::lng_premium_unlock_reactions() ? tr::lng_premium_unlock_reactions()
: (section == PremiumPreview::Stickers) : (section == PremiumFeature::Stickers)
? tr::lng_premium_unlock_stickers() ? tr::lng_premium_unlock_stickers()
: (section == PremiumPreview::AnimatedEmoji) : (section == PremiumFeature::AnimatedEmoji)
? tr::lng_premium_unlock_emoji() ? tr::lng_premium_unlock_emoji()
: (section == PremiumPreview::EmojiStatus) : (section == PremiumFeature::EmojiStatus)
? tr::lng_premium_unlock_status() ? tr::lng_premium_unlock_status()
: tr::lng_premium_more_about(); : tr::lng_premium_more_about();
}) | rpl::flatten_latest(); }) | rpl::flatten_latest();
@ -1207,18 +1251,25 @@ void Show(
descriptor.shownCallback(raw); descriptor.shownCallback(raw);
} }
return; return;
} else if (descriptor.section == PremiumPreview::DoubleLimits) { } else if (descriptor.section == PremiumFeature::DoubleLimits) {
show->showBox(Box([=](not_null<Ui::GenericBox*> box) { show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
DoubledLimitsPreviewBox(box, &show->session()); DoubledLimitsPreviewBox(box, &show->session());
DecorateListPromoBox(box, show, descriptor); DecorateListPromoBox(box, show, descriptor);
})); }));
return; return;
} else if (descriptor.section == PremiumPreview::Stories) { } else if (descriptor.section == PremiumFeature::Stories) {
show->showBox(Box([=](not_null<Ui::GenericBox*> box) { show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
UpgradedStoriesPreviewBox(box, &show->session()); UpgradedStoriesPreviewBox(box, &show->session());
DecorateListPromoBox(box, show, descriptor); DecorateListPromoBox(box, show, descriptor);
})); }));
return; return;
} else if (descriptor.section == PremiumFeature::Business) {
const auto window = show->resolveWindow(
ChatHelpers::WindowUsage::PremiumPromo);
if (window) {
Settings::ShowBusiness(window);
}
return;
} }
auto &list = Preloads(); auto &list = Preloads();
for (auto i = begin(list); i != end(list);) { for (auto i = begin(list); i != end(list);) {
@ -1286,21 +1337,21 @@ void ShowStickerPreviewBox(
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<ChatHelpers::Show> show,
not_null<DocumentData*> document) { not_null<DocumentData*> document) {
Show(std::move(show), Descriptor{ Show(std::move(show), Descriptor{
.section = PremiumPreview::Stickers, .section = PremiumFeature::Stickers,
.requestedSticker = document, .requestedSticker = document,
}); });
} }
void ShowPremiumPreviewBox( void ShowPremiumPreviewBox(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
PremiumPreview section, PremiumFeature section,
Fn<void(not_null<Ui::BoxContent*>)> shown) { Fn<void(not_null<Ui::BoxContent*>)> shown) {
ShowPremiumPreviewBox(controller->uiShow(), section, std::move(shown)); ShowPremiumPreviewBox(controller->uiShow(), section, std::move(shown));
} }
void ShowPremiumPreviewBox( void ShowPremiumPreviewBox(
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<ChatHelpers::Show> show,
PremiumPreview section, PremiumFeature section,
Fn<void(not_null<Ui::BoxContent*>)> shown, Fn<void(not_null<Ui::BoxContent*>)> shown,
bool hideSubscriptionButton) { bool hideSubscriptionButton) {
Show(std::move(show), Descriptor{ Show(std::move(show), Descriptor{
@ -1312,7 +1363,7 @@ void ShowPremiumPreviewBox(
void ShowPremiumPreviewToBuy( void ShowPremiumPreviewToBuy(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
PremiumPreview section, PremiumFeature section,
Fn<void()> hiddenCallback) { Fn<void()> hiddenCallback) {
Show(controller->uiShow(), Descriptor{ Show(controller->uiShow(), Descriptor{
.section = section, .section = section,

View file

@ -45,7 +45,8 @@ void UpgradedStoriesPreviewBox(
not_null<Ui::GenericBox*> box, not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session); not_null<Main::Session*> session);
enum class PremiumPreview { enum class PremiumFeature {
// Premium features.
Stories, Stories,
DoubleLimits, DoubleLimits,
MoreUpload, MoreUpload,
@ -64,24 +65,33 @@ enum class PremiumPreview {
TagsForMessages, TagsForMessages,
LastSeen, LastSeen,
MessagePrivacy, MessagePrivacy,
Business,
// Business features.
BusinessLocation,
BusinessHours,
QuickReplies,
GreetingMessage,
AwayMessage,
BusinessBots,
kCount, kCount,
}; };
void ShowPremiumPreviewBox( void ShowPremiumPreviewBox(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
PremiumPreview section, PremiumFeature section,
Fn<void(not_null<Ui::BoxContent*>)> shown = nullptr); Fn<void(not_null<Ui::BoxContent*>)> shown = nullptr);
void ShowPremiumPreviewBox( void ShowPremiumPreviewBox(
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<ChatHelpers::Show> show,
PremiumPreview section, PremiumFeature section,
Fn<void(not_null<Ui::BoxContent*>)> shown = nullptr, Fn<void(not_null<Ui::BoxContent*>)> shown = nullptr,
bool hideSubscriptionButton = false); bool hideSubscriptionButton = false);
void ShowPremiumPreviewToBuy( void ShowPremiumPreviewToBuy(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
PremiumPreview section, PremiumFeature section,
Fn<void()> hiddenCallback = nullptr); Fn<void()> hiddenCallback = nullptr);
void PremiumUnavailableBox(not_null<Ui::GenericBox*> box); void PremiumUnavailableBox(not_null<Ui::GenericBox*> box);

View file

@ -77,20 +77,15 @@ AdminLog::OwnedItem GenerateItem(
const QString &text) { const QString &text) {
Expects(history->peer->isUser()); Expects(history->peer->isUser());
const auto item = history->addNewLocalMessage( const auto item = history->addNewLocalMessage({
history->nextNonHistoryEntryId(), .id = history->nextNonHistoryEntryId(),
(MessageFlag::FakeHistoryItem .flags = (MessageFlag::FakeHistoryItem
| MessageFlag::HasFromId | MessageFlag::HasFromId
| MessageFlag::HasReplyInfo), | MessageFlag::HasReplyInfo),
UserId(), // via .from = from,
FullReplyTo{ .messageId = replyTo }, .replyTo = FullReplyTo{ .messageId = replyTo },
base::unixtime::now(), // date .date = base::unixtime::now(),
from, }, TextWithEntities{ .text = text }, MTP_messageMediaEmpty());
QString(), // postAuthor
TextWithEntities{ .text = text },
MTP_messageMediaEmpty(),
HistoryMessageMarkupData(),
uint64(0)); // groupedId
return AdminLog::OwnedItem(delegate, item); return AdminLog::OwnedItem(delegate, item);
} }

View file

@ -327,7 +327,7 @@ void RingtonesBox(
box->setWidth(st::boxWideWidth); box->setWidth(st::boxWideWidth);
box->addButton(tr::lng_settings_save(), [=] { box->addButton(tr::lng_settings_save(), [=] {
const auto value = state->group->value(); const auto value = state->group->current();
auto sound = (value == kDefaultValue) auto sound = (value == kDefaultValue)
? Data::NotifySound() ? Data::NotifySound()
: (value == kNoSoundValue) : (value == kNoSoundValue)

View file

@ -95,7 +95,7 @@ void SelfDestructionBox::showContent() {
clearButtons(); clearButtons();
addButton(tr::lng_settings_save(), [=] { addButton(tr::lng_settings_save(), [=] {
const auto value = _ttlGroup->value(); const auto value = _ttlGroup->current();
switch (_type) { switch (_type) {
case Type::Account: case Type::Account:
_session->api().selfDestruct().updateAccountTTL(value); _session->api().selfDestruct().updateAccountTTL(value);

View file

@ -1172,7 +1172,7 @@ void SendFilesBox::setupEmojiPanel() {
_captionToPeer, _captionToPeer,
data.document) data.document)
: (_limits & SendFilesAllow::EmojiWithoutPremium))) { : (_limits & SendFilesAllow::EmojiWithoutPremium))) {
ShowPremiumPreviewBox(_show, PremiumPreview::AnimatedEmoji); ShowPremiumPreviewBox(_show, PremiumFeature::AnimatedEmoji);
} else { } else {
Data::InsertCustomEmoji(_caption.data(), data.document); Data::InsertCustomEmoji(_caption.data(), data.document);
} }

View file

@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/share_message_phrase_factory.h" #include "chat_helpers/share_message_phrase_factory.h"
#include "data/business/data_shortcut_messages.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_game.h" #include "data/data_game.h"
#include "data/data_histories.h" #include "data/data_histories.h"
@ -1543,11 +1544,15 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
const auto threadHistory = thread->owningHistory(); const auto threadHistory = thread->owningHistory();
histories.sendRequest(threadHistory, requestType, [=]( histories.sendRequest(threadHistory, requestType, [=](
Fn<void()> finish) { Fn<void()> finish) {
auto &api = threadHistory->session().api(); const auto session = &threadHistory->session();
auto &api = session->api();
const auto sendFlags = commonSendFlags const auto sendFlags = commonSendFlags
| (topMsgId ? Flag::f_top_msg_id : Flag(0)) | (topMsgId ? Flag::f_top_msg_id : Flag(0))
| (ShouldSendSilent(peer, options) | (ShouldSendSilent(peer, options)
? Flag::f_silent ? Flag::f_silent
: Flag(0))
| (options.shortcutId
? Flag::f_quick_reply_shortcut
: Flag(0)); : Flag(0));
threadHistory->sendRequestId = api.request( threadHistory->sendRequestId = api.request(
MTPmessages_ForwardMessages( MTPmessages_ForwardMessages(
@ -1558,7 +1563,8 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
peer->input, peer->input,
MTP_int(topMsgId), MTP_int(topMsgId),
MTP_int(options.scheduled), MTP_int(options.scheduled),
MTP_inputPeerEmpty() // send_as MTP_inputPeerEmpty(), // send_as
Data::ShortcutIdToMTP(session, options.shortcutId)
)).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);

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_document_media.h" #include "data/data_document_media.h"
#include "data/data_premium_limits.h"
#include "data/stickers/data_stickers.h" #include "data/stickers/data_stickers.h"
#include "core/application.h" #include "core/application.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
@ -40,8 +41,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/unread_badge_paint.h" #include "ui/unread_badge_paint.h"
#include "media/clip/media_clip_reader.h" #include "media/clip/media_clip_reader.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
@ -2050,10 +2049,8 @@ void StickersBox::Inner::checkGroupLevel(Fn<void()> done) {
return std::optional<Ui::AskBoostReason>(); return std::optional<Ui::AskBoostReason>();
} }
_checkingGroupLevel = false; _checkingGroupLevel = false;
const auto appConfig = &peer->session().account().appConfig(); const auto required = Data::LevelLimits(
const auto required = appConfig->get<int>( &peer->session()).groupEmojiStickersLevelMin();
"group_emoji_stickers_level_min",
4);
if (level >= required) { if (level >= required) {
save(); save();
return std::optional<Ui::AskBoostReason>(); return std::optional<Ui::AskBoostReason>();

View file

@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h" #include "main/main_session.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "platform/platform_specific.h" #include "platform/platform_specific.h"
#include "base/event_filter.h"
#include "base/platform/base_platform_info.h" #include "base/platform/base_platform_info.h"
#include "base/power_save_blocker.h" #include "base/power_save_blocker.h"
#include "media/streaming/media_streaming_utility.h" #include "media/streaming/media_streaming_utility.h"
@ -147,17 +148,18 @@ void Panel::initWindow() {
window()->setTitle(_user->name()); window()->setTitle(_user->name());
window()->setTitleStyle(st::callTitle); window()->setTitleStyle(st::callTitle);
window()->events( base::install_event_filter(window().get(), [=](not_null<QEvent*> e) {
) | rpl::start_with_next([=](not_null<QEvent*> e) { if (e->type() == QEvent::Close && handleClose()) {
if (e->type() == QEvent::Close) { e->ignore();
handleClose(); return base::EventFilterResult::Cancel;
} else if (e->type() == QEvent::KeyPress) { } else if (e->type() == QEvent::KeyPress) {
if ((static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Escape) if ((static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Escape)
&& window()->isFullScreen()) { && window()->isFullScreen()) {
window()->showNormal(); window()->showNormal();
} }
} }
}, window()->lifetime()); return base::EventFilterResult::Continue;
});
window()->setBodyTitleArea([=](QPoint widgetPoint) { window()->setBodyTitleArea([=](QPoint widgetPoint) {
using Flag = Ui::WindowTitleHitTestFlag; using Flag = Ui::WindowTitleHitTestFlag;
@ -828,10 +830,12 @@ void Panel::paint(QRect clip) {
} }
} }
void Panel::handleClose() { bool Panel::handleClose() const {
if (_call) { if (_call) {
_call->hangup(); window()->hide();
return true;
} }
return false;
} }
not_null<Ui::RpWindow*> Panel::window() const { not_null<Ui::RpWindow*> Panel::window() const {

View file

@ -106,7 +106,7 @@ private:
void initLayout(); void initLayout();
void initGeometry(); void initGeometry();
void handleClose(); [[nodiscard]] bool handleClose() const;
void updateControlsGeometry(); void updateControlsGeometry();
void updateHangupGeometry(); void updateHangupGeometry();

View file

@ -608,7 +608,7 @@ defaultComposeIcons: ComposeIcons {
stripBubble: icon{ stripBubble: icon{
{ "chat/reactions_bubble_shadow", windowShadowFg }, { "chat/reactions_bubble_shadow", windowShadowFg },
{ "chat/reactions_bubble", windowBg }, { "chat/reactions_bubble", emojiPanBg },
}; };
stripExpandPanel: icon{ stripExpandPanel: icon{
{ "chat/reactions_round_big", windowBgRipple }, { "chat/reactions_round_big", windowBgRipple },

View file

@ -23,6 +23,7 @@ struct ComposeFeatures {
bool autocompleteHashtags = true; bool autocompleteHashtags = true;
bool autocompleteMentions = true; bool autocompleteMentions = true;
bool autocompleteCommands = true; bool autocompleteCommands = true;
bool commonTabbedPanel = true;
}; };
} // namespace ChatHelpers } // namespace ChatHelpers

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