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.
run: |
sudo chown -R `whoami`:admin /usr/local/share
brew install automake ninja pkg-config
brew install automake ninja pkg-config nasm meson
# Disable spotlight.
sudo mdutil -a -i off

3
.gitmodules vendored
View file

@ -100,3 +100,6 @@
[submodule "Telegram/ThirdParty/libprisma"]
path = Telegram/ThirdParty/libprisma
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_chats_list.cpp
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.h
boxes/peers/add_bot_to_chat_box.cpp
@ -514,6 +516,14 @@ PRIVATE
core/version.h
countries/countries_manager.cpp
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.h
data/notify/data_peer_notify_settings.cpp
@ -1345,6 +1355,22 @@ PRIVATE
profile/profile_block_widget.h
profile/profile_cover_drop_area.cpp
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.h
settings/cloud_password/settings_cloud_password_email.cpp
@ -1363,6 +1389,8 @@ PRIVATE
settings/settings_advanced.h
settings/settings_blocked_peers.cpp
settings/settings_blocked_peers.h
settings/settings_business.cpp
settings/settings_business.h
settings/settings_chat.cpp
settings/settings_chat.h
settings/settings_calls.cpp
@ -1722,7 +1750,7 @@ else()
)
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)
target_link_libraries(Telegram
@ -1796,6 +1824,7 @@ set_target_properties(Telegram PROPERTIES
XCODE_ATTRIBUTE_ALWAYS_SEARCH_USER_PATHS NO
XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY libc++
XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS --deep
XCODE_ATTRIBUTE_CLANG_DEBUG_INFORMATION_LEVEL $<IF:$<CONFIG:Debug>,default,line-tables-only>
)
set(entitlement_sources
"${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;
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;
}
function ShowTextCopied(content) {
navigator.clipboard.writeText(content);
ShowToast("Text copied to clipboard.");
return false;
}
function ShowSpoiler(target) {
if (target.classList.contains("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_location_label" = "Location";
"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_bot_title" = "Bot 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_subtitle_translation" = "Real-Time Translation";
"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_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";
@ -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_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_group_button" = "Boost Group";
"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_read_hidden" = "read";
"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_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_groups" = "Groups";
"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_no_archived" = "Archived";
"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#other" = "{count} subscriptions {duration}";
"lng_contact_add" = "Add";
"lng_contact_send_message" = "message";
// Wnd specific
"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_start.tgs">../../animations/voice_ttl_start.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>
</RCC>

View file

@ -3,6 +3,7 @@
<file alias="art/background.tgv">../../art/background.tgv</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/business_logo.png">../../art/business_logo.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/themeimage.jpg">../../art/themeimage.jpg</file>

View file

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

View file

@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,15,0,0
PRODUCTVERSION 4,15,0,0
FILEVERSION 4,15,2,0
PRODUCTVERSION 4,15,2,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop"
VALUE "FileVersion", "4.15.0.0"
VALUE "FileVersion", "4.15.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "4.15.0.0"
VALUE "ProductVersion", "4.15.2.0"
END
END
BLOCK "VarFileInfo"

View file

@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,15,0,0
PRODUCTVERSION 4,15,0,0
FILEVERSION 4,15,2,0
PRODUCTVERSION 4,15,2,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop Updater"
VALUE "FileVersion", "4.15.0.0"
VALUE "FileVersion", "4.15.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "AyuGram Desktop"
VALUE "ProductVersion", "4.15.0.0"
VALUE "ProductVersion", "4.15.2.0"
END
END
BLOCK "VarFileInfo"

View file

@ -22,6 +22,7 @@ inline constexpr auto kScheduledUntilOnlineTimestamp = TimeId(0x7FFFFFFE);
struct SendOptions {
PeerData *sendAs = nullptr;
TimeId scheduled = 0;
BusinessShortcutId shortcutId = 0;
bool silent = false;
bool handleSupportSwitch = 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_text_entities.h"
#include "ui/boxes/confirm_box.h"
#include "data/business/data_shortcut_messages.h"
#include "data/data_histories.h"
#include "data/data_scheduled_messages.h"
#include "data/data_session.h"
@ -88,10 +89,15 @@ mtpRequestId EditMessage(
: emptyFlag)
| (options.scheduled
? MTPmessages_EditMessage::Flag::f_schedule_date
: emptyFlag)
| (item->isBusinessShortcut()
? MTPmessages_EditMessage::Flag::f_quick_reply_shortcut_id
: emptyFlag);
const auto id = item->isScheduled()
? session->data().scheduledMessages().lookupId(item)
: item->isBusinessShortcut()
? session->data().shortcutMessages().lookupId(item)
: item->id;
return api->request(MTPmessages_EditMessage(
MTP_flags(flags),
@ -101,7 +107,8 @@ mtpRequestId EditMessage(
inputMedia.value_or(Data::WebPageForMTP(webpage, text.isEmpty())),
MTPReplyMarkup(),
sentEntities,
MTP_int(options.scheduled)
MTP_int(options.scheduled),
MTP_int(item->shortcutId())
)).done([=](
const MTPUpdates &result,
[[maybe_unused]] mtpRequestId requestId) {

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_updates.h"
#include "apiwrap.h"
#include "base/random.h"
#include "data/business/data_shortcut_messages.h"
#include "data/data_changes.h"
#include "data/data_histories.h"
#include "data/data_poll.h"
@ -69,6 +70,9 @@ void Polls::create(
if (action.options.scheduled) {
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;
if (sendAs) {
sendFlags |= MTPmessages_SendMedia::Flag::f_send_as;
@ -89,7 +93,8 @@ void Polls::create(
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(),
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) {
if (clearCloudDraft) {
history->finishSavingCloudDraft(
@ -184,7 +189,8 @@ void Polls::close(not_null<HistoryItem*> item) {
PollDataToInputMedia(poll, true),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(),
MTP_int(0) // schedule_date
MTP_int(0), // schedule_date
MTPint() // quick_reply_shortcut_id
)).done([=](const MTPUpdates &result) {
_pollCloseRequestIds.erase(itemId);
_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 "base/random.h"
#include "base/unixtime.h"
#include "data/business/data_shortcut_messages.h"
#include "data/data_document.h"
#include "data/data_photo.h"
#include "data/data_channel.h" // ChannelData::addsSignature.
@ -84,20 +85,21 @@ void SendExistingMedia(
? (*localMessageId)
: session->data().nextLocalMessageId());
const auto randomId = base::RandomValue<uint64>();
const auto &action = message.action;
auto flags = NewMessageFlags(peer);
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (message.action.replyTo) {
if (action.replyTo) {
flags |= MessageFlag::HasReplyInfo;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
}
const auto anonymousPost = peer->amAnonymous();
const auto silentPost = ShouldSendSilent(peer, message.action.options);
InnerFillMessagePostFlags(message.action.options, peer, flags);
const auto silentPost = ShouldSendSilent(peer, action.options);
InnerFillMessagePostFlags(action.options, peer, flags);
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
const auto sendAs = message.action.options.sendAs;
const auto sendAs = action.options.sendAs;
const auto messageFromId = sendAs
? sendAs->id
: anonymousPost
@ -124,32 +126,34 @@ void SendExistingMedia(
}
const auto captionText = caption.text;
if (message.action.options.scheduled) {
if (action.options.scheduled) {
flags |= MessageFlag::IsOrWasScheduled;
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);
const auto viaBotId = UserId();
history->addNewLocalMessage(
newId.msg,
flags,
viaBotId,
message.action.replyTo,
HistoryItem::NewMessageDate(message.action.options.scheduled),
messageFromId,
messagePostAuthor,
media,
caption,
HistoryMessageMarkupData());
history->addNewLocalMessage({
.id = newId.msg,
.flags = flags,
.from = messageFromId,
.replyTo = action.replyTo,
.date = HistoryItem::NewMessageDate(action.options),
.shortcutId = action.options.shortcutId,
.postAuthor = messagePostAuthor,
}, media, caption);
const auto performRequest = [=](const auto &repeatRequest) -> void {
auto &histories = history->owner().histories();
const auto session = &history->session();
const auto usedFileReference = media->fileReference();
histories.sendPreparedMessage(
history,
message.action.replyTo,
action.replyTo,
randomId,
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(sendFlags),
@ -160,8 +164,9 @@ void SendExistingMedia(
MTP_long(randomId),
MTPReplyMarkup(),
sentEntities,
MTP_int(message.action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId)
), [=](const MTPUpdates &result, const MTP::Response &response) {
}, [=](const MTP::Error &error, const MTP::Response &response) {
if (error.code() == 400
@ -180,7 +185,7 @@ void SendExistingMedia(
};
performRequest(performRequest);
api->finishForwarding(message.action);
api->finishForwarding(action);
}
} // namespace
@ -259,7 +264,10 @@ bool SendDice(MessageToSend &message) {
message.textWithTags = TextWithTags();
message.action.clearDraft = false;
message.action.generateLocal = true;
api->sendAction(message.action);
const auto &action = message.action;
api->sendAction(action);
const auto newId = FullMsgId(
peer->id,
@ -269,17 +277,17 @@ bool SendDice(MessageToSend &message) {
auto &histories = history->owner().histories();
auto flags = NewMessageFlags(peer);
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (message.action.replyTo) {
if (action.replyTo) {
flags |= MessageFlag::HasReplyInfo;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
}
const auto anonymousPost = peer->amAnonymous();
const auto silentPost = ShouldSendSilent(peer, message.action.options);
InnerFillMessagePostFlags(message.action.options, peer, flags);
const auto silentPost = ShouldSendSilent(peer, action.options);
InnerFillMessagePostFlags(action.options, peer, flags);
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
const auto sendAs = message.action.options.sendAs;
const auto sendAs = action.options.sendAs;
const auto messageFromId = sendAs
? sendAs->id
: anonymousPost
@ -292,28 +300,31 @@ bool SendDice(MessageToSend &message) {
? session->user()->name()
: QString();
if (message.action.options.scheduled) {
if (action.options.scheduled) {
flags |= MessageFlag::IsOrWasScheduled;
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);
const auto viaBotId = UserId();
history->addNewLocalMessage(
newId.msg,
flags,
viaBotId,
message.action.replyTo,
HistoryItem::NewMessageDate(message.action.options.scheduled),
messageFromId,
messagePostAuthor,
TextWithEntities(),
MTP_messageMediaDice(MTP_int(0), MTP_string(emoji)),
HistoryMessageMarkupData());
history->addNewLocalMessage({
.id = newId.msg,
.flags = flags,
.from = messageFromId,
.replyTo = action.replyTo,
.date = HistoryItem::NewMessageDate(action.options),
.shortcutId = action.options.shortcutId,
.postAuthor = messagePostAuthor,
}, TextWithEntities(), MTP_messageMediaDice(
MTP_int(0),
MTP_string(emoji)));
histories.sendPreparedMessage(
history,
message.action.replyTo,
action.replyTo,
randomId,
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(sendFlags),
@ -324,13 +335,14 @@ bool SendDice(MessageToSend &message) {
MTP_long(randomId),
MTPReplyMarkup(),
MTP_vector<MTPMessageEntity>(),
MTP_int(message.action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(session, action.options.shortcutId)
), [=](const MTPUpdates &result, const MTP::Response &response) {
}, [=](const MTP::Error &error, const MTP::Response &response) {
api->sendMessageFail(error, peer, randomId, newId);
});
api->finishForwarding(message.action);
api->finishForwarding(action);
return true;
}
@ -406,7 +418,13 @@ void SendConfirmedFile(
if (file->to.options.scheduled) {
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;
}
if (file->type == SendMediaType::Audio) {
@ -415,8 +433,7 @@ void SendConfirmedFile(
}
}
const auto messageFromId =
file->to.options.sendAs
const auto messageFromId = file->to.options.sendAs
? file->to.options.sendAs->id
: anonymousPost
? PeerId()
@ -486,19 +503,16 @@ void SendConfirmedFile(
edition.savePreviousMedia = true;
itemToEdit->applyEdition(std::move(edition));
} else {
const auto viaBotId = UserId();
history->addNewLocalMessage(
newId.msg,
flags,
viaBotId,
file->to.replyTo,
HistoryItem::NewMessageDate(file->to.options.scheduled),
messageFromId,
messagePostAuthor,
caption,
media,
HistoryMessageMarkupData(),
groupId);
history->addNewLocalMessage({
.id = newId.msg,
.flags = flags,
.from = messageFromId,
.replyTo = file->to.replyTo,
.date = HistoryItem::NewMessageDate(file->to.options),
.shortcutId = file->to.options.shortcutId,
.postAuthor = messagePostAuthor,
.groupedId = groupId,
}, caption, media);
}
if (isEditing) {

View file

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

View file

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

View file

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

View file

@ -81,9 +81,9 @@ void AutoLockBox::prepare() {
const auto timeInput = Ui::CreateChild<Ui::TimeInput>(
this,
(group->value() == kCustom)
(group->current() == kCustom
? TimeString(currentTime)
: kDefaultCustom.utf8(),
: kDefaultCustom.utf8()),
st::autolockTimeField,
st::autolockDateField,
st::scheduleTimeSeparator,
@ -115,7 +115,9 @@ void AutoLockBox::prepare() {
});
rpl::merge(
boxClosing() | rpl::filter([=] { return group->value() == kCustom; }),
boxClosing() | rpl::filter(
[=] { return group->current() == kCustom; }
),
timeInput->submitRequests()
) | rpl::start_with_next([=] {
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_helpers.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 "apiwrap.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_file_origin.h"
#include "data/data_peer_values.h"
#include "data/data_premium_limits.h"
#include "settings/settings_premium.h"
#include "storage/file_upload.h"
#include "storage/localimageloader.h"
@ -82,11 +81,11 @@ constexpr auto kMaxWallPaperSlugLength = 255;
const auto flags = MessageFlag::FakeHistoryItem
| MessageFlag::HasFromId
| (out ? MessageFlag::Outgoing : MessageFlag(0));
const auto item = history->makeMessage(
history->owner().nextLocalMessageId(),
flags,
base::unixtime::now(),
PreparedServiceText{ { text } });
const auto item = history->makeMessage({
.id = history->owner().nextLocalMessageId(),
.flags = flags,
.date = base::unixtime::now(),
}, PreparedServiceText{ { text } });
return AdminLog::OwnedItem(delegate, item);
}
@ -97,24 +96,16 @@ constexpr auto kMaxWallPaperSlugLength = 255;
bool out) {
Expects(history->peer->isUser());
const auto flags = MessageFlag::FakeHistoryItem
| MessageFlag::HasFromId
| (out ? MessageFlag::Outgoing : MessageFlag(0));
const auto replyTo = FullReplyTo();
const auto viaBotId = UserId();
const auto groupedId = uint64();
const auto item = history->makeMessage(
history->nextNonHistoryEntryId(),
flags,
replyTo,
viaBotId,
base::unixtime::now(),
out ? history->session().userId() : peerToUser(history->peer->id),
QString(),
TextWithEntities{ text },
MTP_messageMediaEmpty(),
HistoryMessageMarkupData(),
groupedId);
const auto item = history->makeMessage({
.id = history->nextNonHistoryEntryId(),
.flags = (MessageFlag::FakeHistoryItem
| MessageFlag::HasFromId
| (out ? MessageFlag::Outgoing : MessageFlag(0))),
.from = (out
? history->session().userId()
: peerToUser(history->peer->id)),
.date = base::unixtime::now(),
}, TextWithEntities{ text }, MTP_messageMediaEmpty());
return AdminLog::OwnedItem(delegate, item);
}
@ -699,16 +690,10 @@ void BackgroundPreviewBox::checkLevelForChannel() {
if (!weak) {
return std::optional<Ui::AskBoostReason>();
}
const auto appConfig = &_forPeer->session().account().appConfig();
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 limits = Data::LevelLimits(&_forPeer->session());
const auto required = _paperEmojiId.isEmpty()
? customRequired
: defaultRequired;
? limits.channelCustomWallpaperLevelMin()
: limits.channelWallpaperLevelMin();
if (level >= required) {
applyForPeer(false);
return std::optional<Ui::AskBoostReason>();
@ -791,7 +776,7 @@ void BackgroundPreviewBox::applyForPeer() {
} else {
ShowPremiumPreviewBox(
_controller->uiShow(),
PremiumPreview::Wallpapers);
PremiumFeature::Wallpapers);
}
});
const auto cancel = CreateChild<RoundButton>(

View file

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

View file

@ -44,7 +44,9 @@ void DownloadPathBox::prepare() {
setTitle(tr::lng_download_path_header());
_group->setChangedCallback([this](Directory value) { radioChanged(value); });
_group->setChangedCallback([this](Directory value) {
radioChanged(value);
});
_pathLink->addClickHandler([=] { editPath(); });
if (!_path.isEmpty() && _path != FileDialog::Tmp()) {
@ -54,7 +56,7 @@ void DownloadPathBox::prepare() {
}
void DownloadPathBox::updateControlsVisibility() {
auto custom = (_group->value() == Directory::Custom);
auto custom = (_group->current() == Directory::Custom);
_pathLink->setVisible(custom);
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() {
#ifndef OS_WIN_STORE
auto value = _group->value();
auto value = _group->current();
auto computePath = [this, value] {
if (value == Directory::Custom) {
return _path;

View file

@ -682,7 +682,7 @@ void EditCaptionBox::setupEmojiPanel() {
&& !_controller->session().premium()) {
ShowPremiumPreviewBox(
_controller,
PremiumPreview::AnimatedEmoji);
PremiumFeature::AnimatedEmoji);
} else {
Data::InsertCustomEmoji(_field.get(), data.document);
}
@ -895,6 +895,7 @@ void EditCaptionBox::save() {
auto options = Api::SendOptions();
options.scheduled = item->isScheduled() ? item->date() : 0;
options.shortcutId = item->shortcutId();
if (!_preparedList.files.empty()) {
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 "ui/layers/generic_box.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/painter.h"
#include "ui/vertical_list.h"
#include "history/history.h"
#include "boxes/peer_list_controllers.h"
#include "settings/settings_common.h"
#include "settings/settings_premium.h"
#include "settings/settings_privacy_security.h"
#include "calls/calls_instance.h"
#include "base/binary_guard.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
#include "main/main_session.h"
#include "data/data_user.h"
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "data/data_peer_values.h"
#include "window/window_session_controller.h"
#include "styles/style_settings.h"
#include "styles/style_layers.h"
@ -67,6 +63,32 @@ void CreateRadiobuttonLock(
}, 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
class PrivacyExceptionsBoxController : public ChatsListBoxController {
@ -363,10 +385,29 @@ void EditPrivacyBox::setupContent() {
content,
_controller->optionsTitleKey(),
{ 0, st::settingsPrivacySkipTop, 0, 0 });
addOptionRow(Option::Everyone);
addOptionRow(Option::Contacts);
addOptionRow(Option::CloseFriends);
addOptionRow(Option::Nobody);
const auto options = {
Option::Everyone,
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(
content,
_controller->warning(),
@ -541,7 +582,7 @@ void EditMessagesPrivacyBox(
box->addButton(tr::lng_settings_save(), [=] {
if (controller->session().premium()) {
privacy->updateNewRequirePremium(
group->value() == kOptionPremium);
group->current() == kOptionPremium);
box->closeBox();
} else {
showToast();

View file

@ -88,6 +88,12 @@ public:
virtual void saveAdditional() {
}
[[nodiscard]] virtual Fn<void()> premiumClickedCallback(
Option option,
not_null<Window::SessionController*> controller) {
return nullptr;
}
virtual ~EditPrivacyController() = default;
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_chats_list.h"
#include "boxes/filters/edit_filter_chats_preview.h"
#include "boxes/filters/edit_filter_links.h"
#include "boxes/premium_limits_box.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_peer.h"
#include "data/data_peer_values.h" // Data::AmPremiumValue.
#include "data/data_premium_limits.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "core/application.h"
@ -56,60 +58,6 @@ using Flags = Data::ChatFilter::Flags;
using ExceptionPeersRef = const base::flat_set<not_null<History*>> &;
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 {
not_null<Ui::InputField*> field;
bool custom = false;
@ -167,167 +115,6 @@ not_null<FilterChatsPreview*> SetupChatsPreview(
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(
not_null<Window::SessionController*> window,
not_null<QObject*> context,
@ -338,6 +125,12 @@ void EditExceptions(
const auto include = (options & Flag::Contacts) != Flags(0);
const auto rules = data->current();
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>(
session,
(include
@ -346,9 +139,8 @@ void EditExceptions(
options,
rules.flags() & options,
include ? rules.always() : rules.never(),
[=](int count) {
return Box(FilterChatsLimitBox, session, count, include);
});
limit,
showLimitReached);
const auto rawController = controller.get();
auto initBox = [=](not_null<PeerListBox*> box) {
box->setCloseByOutsideClick(false);

View file

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

View file

@ -43,7 +43,6 @@ class EditFilterChatsListController final : public ChatsListBoxController {
public:
using Flag = Data::ChatFilter::Flag;
using Flags = Data::ChatFilter::Flags;
using LimitBoxFactory = Fn<object_ptr<Ui::BoxContent>(int)>;
EditFilterChatsListController(
not_null<Main::Session*> session,
@ -51,7 +50,8 @@ public:
Flags options,
Flags selected,
const base::flat_set<not_null<History*>> &peers,
LimitBoxFactory limitBox);
int limit,
Fn<void()> showLimitReached);
[[nodiscard]] Main::Session &session() const override;
[[nodiscard]] Flags chosenOptions() const {
@ -72,7 +72,7 @@ private:
void updateTitle();
const not_null<Main::Session*> _session;
const LimitBoxFactory _limitBox;
const Fn<void()> _showLimitReached;
rpl::producer<QString> _title;
base::flat_set<not_null<History*>> _peers;
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,
const Data::ChatFilter &filter) {
using Flag = Data::ChatFilter::Flag;
const auto listflags = Flag::Chatlist | Flag::HasMyLinks;
if (!filter.never().empty() || (filter.flags() & ~listflags)) {
if (!filter.never().empty() || (filter.flags() & Flag::RulesMask)) {
window->showToast(tr::lng_filters_link_cant(tr::now));
return false;
}

View file

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

View file

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

View file

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

View file

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

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/background_box.h"
#include "boxes/stickers_box.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_stickers.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_file_origin.h"
#include "data/data_peer.h"
#include "data/data_premium_limits.h"
#include "data/data_session.h"
#include "data/data_web_page.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 "lottie/lottie_icon.h"
#include "lottie/lottie_single_player.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "settings/settings_common.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_theme.h"
#include "ui/effects/path_shift_gradient.h"
#include "ui/effects/premium_graphics.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/buttons.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/vertical_list.h"
#include "window/themes/window_theme.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(
not_null<QWidget*> parent,
std::shared_ptr<Ui::ChatStyle> style,
@ -295,47 +319,36 @@ PreviewWrap::PreviewWrap(
, _delegate(std::make_unique<PreviewDelegate>(box, _style.get(), [=] {
update();
}))
, _replyToItem(_history->addNewLocalMessage(
_history->nextNonHistoryEntryId(),
(MessageFlag::FakeHistoryItem
, _replyToItem(_history->addNewLocalMessage({
.id = _history->nextNonHistoryEntryId(),
.flags = (MessageFlag::FakeHistoryItem
| MessageFlag::HasFromId
| MessageFlag::Post),
UserId(), // via
FullReplyTo(),
base::unixtime::now(), // date
_fake->id,
QString(), // postAuthor
TextWithEntities{ _peer->isSelf()
? tr::lng_settings_color_reply(tr::now)
: tr::lng_settings_color_reply_channel(tr::now),
},
MTP_messageMediaEmpty(),
HistoryMessageMarkupData(),
uint64(0)))
, _replyItem(_history->addNewLocalMessage(
_history->nextNonHistoryEntryId(),
(MessageFlag::FakeHistoryItem
.from = _fake->id,
.date = base::unixtime::now(),
}, TextWithEntities{ _peer->isSelf()
? tr::lng_settings_color_reply(tr::now)
: tr::lng_settings_color_reply_channel(tr::now),
}, MTP_messageMediaEmpty()))
, _replyItem(_history->addNewLocalMessage({
.id = _history->nextNonHistoryEntryId(),
.flags = (MessageFlag::FakeHistoryItem
| MessageFlag::HasFromId
| MessageFlag::HasReplyInfo
| MessageFlag::Post),
UserId(), // via
FullReplyTo{ .messageId = _replyToItem->fullId() },
base::unixtime::now(), // date
_fake->id,
QString(), // postAuthor
TextWithEntities{ _peer->isSelf()
? tr::lng_settings_color_text(tr::now)
: tr::lng_settings_color_text_channel(tr::now),
},
MTP_messageMediaWebPage(
.from = _fake->id,
.replyTo = FullReplyTo{.messageId = _replyToItem->fullId() },
.date = base::unixtime::now(),
}, TextWithEntities{ _peer->isSelf()
? tr::lng_settings_color_text(tr::now)
: tr::lng_settings_color_text_channel(tr::now),
}, MTP_messageMediaWebPage(
MTP_flags(0),
MTP_webPagePending(
MTP_flags(0),
MTP_webPagePending(
MTP_flags(0),
MTP_long(_webpage->id),
MTPstring(),
MTP_int(0))),
HistoryMessageMarkupData(),
uint64(0)))
MTP_long(_webpage->id),
MTPstring(),
MTP_int(0)))))
, _element(_replyItem->createView(_delegate.get()))
, _position(0, st::msgMargin.bottom()) {
_style->apply(_theme.get());
@ -437,6 +450,108 @@ HistoryView::Context PreviewDelegate::elementContext() {
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 {
uint8 colorIndex = 0;
DocumentId backgroundEmojiId = 0;
@ -541,16 +656,13 @@ void Apply(
: peerColors->requiredChannelLevelFor(
peer->id,
values.colorIndex);
const auto limits = Data::LevelLimits(&peer->session());
const auto iconRequired = values.backgroundEmojiId
? session->account().appConfig().get<int>(
"channel_bg_icon_level_min",
5)
? limits.channelBgIconLevelMin()
: 0;
const auto statusRequired = (values.statusChanged
&& values.statusId)
? session->account().appConfig().get<int>(
"channel_emoji_status_level_min",
8)
? limits.channelEmojiStatusLevelMin()
: 0;
const auto required = std::max({
colorRequired,
@ -726,6 +838,7 @@ struct ButtonWithEmoji {
not_null<Ui::RpWidget*> parent,
std::shared_ptr<ChatHelpers::Show> show,
std::shared_ptr<Ui::ChatStyle> style,
not_null<PeerData*> peer,
rpl::producer<uint8> colorIndexValue,
rpl::producer<DocumentId> emojiIdValue,
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;
}
[[nodiscard]] object_ptr<Ui::SettingsButton> CreateEmojiStatusButton(
not_null<Ui::RpWidget*> parent,
std::shared_ptr<ChatHelpers::Show> show,
not_null<ChannelData*> channel,
rpl::producer<DocumentId> statusIdValue,
Fn<void(DocumentId,TimeId)> statusIdChosen,
bool group) {
const auto button = ButtonStyleWithRightEmoji(parent);
const auto &phrase = group
? tr::lng_edit_channel_status_group
: tr::lng_edit_channel_status;
auto result = Settings::CreateButtonWithIcon(
parent,
(group
? tr::lng_edit_channel_status_group()
: tr::lng_edit_channel_status()),
phrase(),
*button.st,
{ &st::menuBlueIconEmojiStatus });
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;
}
@ -1036,6 +1172,14 @@ struct ButtonWithEmoji {
}
}, right->lifetime());
AddLevelBadge(
Data::LevelLimits(&channel->session()).groupEmojiStickersLevelMin(),
raw,
right,
channel,
button.st->padding,
tr::lng_group_emoji());
return result;
}
@ -1068,38 +1212,15 @@ void EditPeerColorBox(
state->index = peer->colorIndex();
state->emojiId = peer->backgroundEmojiId();
state->statusId = peer->emojiStatusId();
if (group) {
const auto divider = Ui::CreateChild<Ui::BoxContentDivider>(
box.get());
const auto verticalLayout = box->verticalLayout()->add(
object_ptr<Ui::VerticalLayout>(box.get()));
auto icon = CreateLottieIcon(
verticalLayout,
{
.name = u"palette"_q,
.sizeOverride = {
st::settingsCloudPasswordIconSize,
st::settingsCloudPasswordIconSize,
},
},
st::peerAppearanceIconPadding);
box->setShowFinishedCallback([animate = std::move(icon.animate)] {
animate(anim::repeat::once);
Settings::AddDividerTextWithLottie(box->verticalLayout(), {
.lottie = u"palette"_q,
.lottieSize = st::settingsCloudPasswordIconSize,
.lottieMargins = st::peerAppearanceIconPadding,
.showFinished = box->showFinishes(),
.about = tr::lng_boost_group_about(Ui::Text::WithEntities),
.aboutMargins = st::peerAppearanceCoverLabelMargin,
});
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 {
box->addRow(object_ptr<PreviewWrap>(
box,
@ -1135,6 +1256,7 @@ void EditPeerColorBox(
container,
show,
style,
peer,
state->index.value(),
state->emojiId.value(),
[=](DocumentId id) { state->emojiId = id; }));
@ -1150,20 +1272,35 @@ void EditPeerColorBox(
if (const auto channel = peer->asChannel()) {
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,
(group
? tr::lng_edit_channel_wallpaper_group()
: tr::lng_edit_channel_wallpaper()),
phrase(),
st::peerAppearanceButton,
{ &st::menuBlueIconWallpaper }
)->setClickedCallback([=] {
);
button->setClickedCallback([=] {
const auto usage = ChatHelpers::WindowUsage::PremiumPromo;
if (const auto strong = show->resolveWindow(usage)) {
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::AddDividerText(
container,
@ -1201,6 +1338,7 @@ void EditPeerColorBox(
container->add(CreateEmojiStatusButton(
container,
show,
channel,
state->statusId.value(),
[=](DocumentId id, TimeId until) {
state->statusId = id;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1172,7 +1172,7 @@ void SendFilesBox::setupEmojiPanel() {
_captionToPeer,
data.document)
: (_limits & SendFilesAllow::EmojiWithoutPremium))) {
ShowPremiumPreviewBox(_show, PremiumPreview::AnimatedEmoji);
ShowPremiumPreviewBox(_show, PremiumFeature::AnimatedEmoji);
} else {
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 "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/share_message_phrase_factory.h"
#include "data/business/data_shortcut_messages.h"
#include "data/data_channel.h"
#include "data/data_game.h"
#include "data/data_histories.h"
@ -1543,11 +1544,15 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
const auto threadHistory = thread->owningHistory();
histories.sendRequest(threadHistory, requestType, [=](
Fn<void()> finish) {
auto &api = threadHistory->session().api();
const auto session = &threadHistory->session();
auto &api = session->api();
const auto sendFlags = commonSendFlags
| (topMsgId ? Flag::f_top_msg_id : Flag(0))
| (ShouldSendSilent(peer, options)
? Flag::f_silent
: Flag(0))
| (options.shortcutId
? Flag::f_quick_reply_shortcut
: Flag(0));
threadHistory->sendRequestId = api.request(
MTPmessages_ForwardMessages(
@ -1558,7 +1563,8 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
peer->input,
MTP_int(topMsgId),
MTP_int(options.scheduled),
MTP_inputPeerEmpty() // send_as
MTP_inputPeerEmpty(), // send_as
Data::ShortcutIdToMTP(session, options.shortcutId)
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
threadHistory->session().api().applyUpdates(updates);
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_file_origin.h"
#include "data/data_document_media.h"
#include "data/data_premium_limits.h"
#include "data/stickers/data_stickers.h"
#include "core/application.h"
#include "lang/lang_keys.h"
@ -40,8 +41,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/painter.h"
#include "ui/unread_badge_paint.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 "styles/style_layers.h"
#include "styles/style_boxes.h"
@ -2050,10 +2049,8 @@ void StickersBox::Inner::checkGroupLevel(Fn<void()> done) {
return std::optional<Ui::AskBoostReason>();
}
_checkingGroupLevel = false;
const auto appConfig = &peer->session().account().appConfig();
const auto required = appConfig->get<int>(
"group_emoji_stickers_level_min",
4);
const auto required = Data::LevelLimits(
&peer->session()).groupEmojiStickersLevelMin();
if (level >= required) {
save();
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 "apiwrap.h"
#include "platform/platform_specific.h"
#include "base/event_filter.h"
#include "base/platform/base_platform_info.h"
#include "base/power_save_blocker.h"
#include "media/streaming/media_streaming_utility.h"
@ -147,17 +148,18 @@ void Panel::initWindow() {
window()->setTitle(_user->name());
window()->setTitleStyle(st::callTitle);
window()->events(
) | rpl::start_with_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::Close) {
handleClose();
base::install_event_filter(window().get(), [=](not_null<QEvent*> e) {
if (e->type() == QEvent::Close && handleClose()) {
e->ignore();
return base::EventFilterResult::Cancel;
} else if (e->type() == QEvent::KeyPress) {
if ((static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Escape)
&& window()->isFullScreen()) {
window()->showNormal();
}
}
}, window()->lifetime());
return base::EventFilterResult::Continue;
});
window()->setBodyTitleArea([=](QPoint widgetPoint) {
using Flag = Ui::WindowTitleHitTestFlag;
@ -828,10 +830,12 @@ void Panel::paint(QRect clip) {
}
}
void Panel::handleClose() {
bool Panel::handleClose() const {
if (_call) {
_call->hangup();
window()->hide();
return true;
}
return false;
}
not_null<Ui::RpWindow*> Panel::window() const {

View file

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

View file

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

View file

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

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