Compare commits

...

81 Commits

Author SHA1 Message Date
John Preston
c04f68f25c Version 5.8.2.
- Improve bottom label color in mini apps.
- Fix some fullscreen issues in mini apps.
- Fix miss-order of archived chats in forward box.
- Fix file dialog for Windows on ARM.
- Fix emoji status pack view from profile regression.
2024-11-19 14:32:15 +04:00
John Preston
e4902efefc Improve fullscreen miniapps on Linux. 2024-11-19 14:24:25 +04:00
John Preston
1267bcd255 Use separate window buttons only with QWindowContainer. 2024-11-19 13:16:54 +04:00
John Preston
0659ccc3f0 Fix user-driven fullscreen change on macOS. 2024-11-19 12:25:27 +04:00
23rd
bd2ae03ab4 Fixed display of long text for open button in web page views. 2024-11-19 10:39:40 +04:00
23rd
244696ae24 Fixed miss-order of archived dialogs in forward box. 2024-11-19 10:39:39 +04:00
John Preston
1438046dd4 Improve bottom label color in mini apps. 2024-11-19 10:37:47 +04:00
John Preston
5c62ba0835 Don't reopen forum on opening a link. 2024-11-18 16:37:22 +04:00
John Preston
20fadfef7f Fix file dialog for Windows on ARM. 2024-11-18 16:01:15 +04:00
John Preston
eed9541f9f Fix Withdraw button text. 2024-11-18 14:25:24 +04:00
John Preston
1594afa389 Fix emoji status pack view regression. 2024-11-18 14:17:01 +04:00
John Preston
9d74d93ed7 Fix some warnings from PVS-Studio.
Fixes #28667.

Some warnings fixed detailed in this post:
https://pvs-studio.com/en/blog/posts/cpp/1186/
2024-11-18 12:50:36 +04:00
John Preston
e4e2f47f8e Adjust build script for Windows Store ARM version. 2024-11-18 01:28:36 +04:00
John Preston
be53bec9b7 Version 5.8.1.
- Fix several possible crashes.
2024-11-17 23:33:45 +04:00
John Preston
bb32c546d4 Fix possible crash in swipe to reply. 2024-11-17 23:24:08 +04:00
John Preston
ecb4ceec7b Fix possible crash in phone click handler. 2024-11-17 23:21:16 +04:00
John Preston
c080bd4c4d Don't resubscribe on folder strip rebuild. 2024-11-17 23:09:42 +04:00
John Preston
39780f49bf Fix folder context menu Remove text color. 2024-11-17 23:00:46 +04:00
John Preston
73349c3c89 Fix crash in chats strip reorder. 2024-11-17 22:52:52 +04:00
John Preston
42a70ff7d0 Fix crash with inline results sending. 2024-11-17 22:41:52 +04:00
John Preston
fa8262cbe9 Version 5.8.
- Updates in Mini Apps platform.
- Chat folders in forward and share boxes.
- Horizontal strip mode for chat folders.
2024-11-17 16:05:22 +04:00
John Preston
7552328cdd Multi-select in mini app send prepared. 2024-11-17 15:08:17 +04:00
John Preston
60f4587d95 Sort birthday gifts to the front. 2024-11-17 15:08:17 +04:00
John Preston
572c074c42 Add gifts-to-profile privacy. 2024-11-17 15:08:17 +04:00
John Preston
3cfbd6a93b Support non-convertible star gifts from bots. 2024-11-17 15:08:16 +04:00
John Preston
d0911b6a45 Send to buy premium from miniapps emoji status. 2024-11-17 15:08:16 +04:00
John Preston
06b85442f8 Fix miniapp downloads in fullscreen. 2024-11-17 15:08:16 +04:00
John Preston
762592daff Allow RTMP for manage call admins. 2024-11-17 15:08:16 +04:00
John Preston
338122793c Implement bot downloads list UI. 2024-11-17 15:08:16 +04:00
John Preston
ef521624a0 Check file by server before downloading. 2024-11-17 15:08:16 +04:00
John Preston
341ab781b2 Implement download of files in miniapps. 2024-11-17 15:08:16 +04:00
John Preston
2fed657940 Make nice share message from miniapp. 2024-11-17 15:08:16 +04:00
John Preston
7bf78b3317 Use initial bot web app header/body colors. 2024-11-17 15:08:16 +04:00
John Preston
2d1fb0562d Prepared messages sharing from miniapp. 2024-11-17 15:08:16 +04:00
John Preston
3d77bff0c9 Support bot emoji status access. 2024-11-17 15:08:15 +04:00
John Preston
4198203a7f Implement emoji status set from miniapps. 2024-11-17 15:08:15 +04:00
John Preston
21487641c1 Support miniapp fullscreen API. 2024-11-17 15:08:15 +04:00
John Preston
c987872be8 Start fullscreen support in SeparatePanel. 2024-11-17 15:08:15 +04:00
John Preston
07e367e1a0 Update API scheme to layer 193. 2024-11-17 15:08:15 +04:00
23rd
db2e45c56e Added ability to go to top of chats list by clicking on selected filter. 2024-11-17 13:43:19 +03:00
23rd
67bbdbfc70 Added ability to change userpic with image from clipboard. 2024-11-17 13:27:57 +03:00
23rd
83df3cba66 Added debug logs for changing chats filters from context menu. 2024-11-17 13:27:57 +03:00
John Preston
f4e2b4bcbd Don't drop the old state of "systemDarkModeEnabled". 2024-11-17 14:07:22 +04:00
Ilya Fedin
6c64c22f83 Don't expand minimum window size for folders
Turn them into horizontal automatically instead
2024-11-17 14:04:27 +04:00
John Preston
702aa944dd Move read/reacted list to a layer. 2024-11-17 14:03:29 +04:00
John Preston
df45edd816 Move channel requests to a layer. 2024-11-17 14:03:29 +04:00
23rd
3f3143514e Added attention style to menu item to delete chats filter. 2024-11-17 11:20:02 +03:00
23rd
3e89910749 Replaced user info with short box for admins in section of kicked users. 2024-11-17 10:46:12 +03:00
23rd
721a642a2f Added date to info who promoted or restricted participants in menu. 2024-11-17 10:40:35 +03:00
23rd
d16ccc9dc5 Fixed display of chats filters strip on removing last chats filter. 2024-11-17 01:52:12 +03:00
23rd
77e7796b3f Fit transactions id from credits history entries to table cell. 2024-11-16 14:43:34 +03:00
23rd
983c949e8c Added ability to provide preloaded credits topup options to list. 2024-11-16 11:39:47 +03:00
23rd
e3f4f60e2d Replaced list of credits topup options with big label in settings. 2024-11-16 11:39:47 +03:00
23rd
33aa904cb7 Added shortcuts to flip photos in media viewer. 2024-11-16 11:39:40 +03:00
23rd
0248be5543 Removed from display extended credits topup options by default. 2024-11-16 07:34:08 +03:00
23rd
f7ca8212aa Moved out down arrow icon of "Show More" button to single place. 2024-11-16 07:34:02 +03:00
23rd
5eb59a1a43 Improved style of tabs for boost gifts in boosts section. 2024-11-16 06:27:07 +03:00
John Preston
7dd1e9bfbe Beta version 5.7.4: Revert accident submodules. 2024-11-16 00:04:20 +04:00
John Preston
067fd25a34 Beta version 5.7.4.
- Enable auto night mode by default.
- Use muted chats folder badge in horizontal strip.
- Fix custom userpic context menu in comments and topics.
- Fix scrolling of chats folders together with the list.
- Fix chats folders list disappearing in some cases.
- Fix topic groups in share box with chats folders.
- Fix bot commands list in some groups.
- Fix crash in channel short info box display.
- Fix crash in bot monetization section opening.
- Fix crash in bot sponsored messages destruction.
2024-11-15 23:59:52 +04:00
John Preston
7cb26ba104 Fix crash in possible ads teardown. 2024-11-15 23:53:59 +04:00
John Preston
a4212cc865 Fix point state detection on media messages. 2024-11-15 23:53:59 +04:00
John Preston
0445f7d6e8 Enable auto-night-mode by default. 2024-11-15 23:53:59 +04:00
John Preston
efe99b3f62 Fix special userpic menu in topics. 2024-11-15 23:53:59 +04:00
23rd
7f2c98f17a Fixed mouse events in chats filters strip with inner padding. 2024-11-15 22:47:39 +03:00
23rd
8f1d215851 Fixed rebuild of chats filters in strip when premium status is changed. 2024-11-15 22:47:39 +03:00
23rd
013b58f6f6 Fixed display of forums in share box with applied chats filter. 2024-11-15 20:04:42 +03:00
23rd
9d3b3476c2 Fixed display of chats filters strip in new windows. 2024-11-15 11:00:35 +03:00
23rd
715874a98f Slightly improved phrase for no results in forward box. 2024-11-15 11:00:30 +03:00
23rd
d2109dd2cb Replaced outer padding for chats filters strip with inner padding. 2024-11-15 11:00:26 +03:00
23rd
ddaf11ed6a Improved style of sponsored messages with media with max width. 2024-11-15 11:00:14 +03:00
23rd
2b5f68003d Fixed pagination of subscriptions in list. 2024-11-15 11:00:07 +03:00
23rd
1a759cc4e7 Fixed crash from short info box for non-users. 2024-11-15 10:59:40 +03:00
23rd
9e83562bf4 Fixed display of pinned chats from main list in forward box. 2024-11-15 10:57:08 +03:00
23rd
c03c19d26f Slightly improved style of chats lists strip in boxes. 2024-11-15 10:57:08 +03:00
23rd
ad9ebdf8e6 Fixed propagation scroll from chats filters strip to chats list. 2024-11-15 10:57:08 +03:00
23rd
9c1701c62a Added support of muted chats filters to chats filters strip. 2024-11-15 10:57:08 +03:00
23rd
edf6c42e9d Added short variant of unfiltered chats list to chats filters strip. 2024-11-15 10:57:08 +03:00
23rd
f0f2a71a87 Fixed disappearing of chats filters strip in forward box after search. 2024-11-15 10:57:08 +03:00
23rd
cf270bd9ce Removed chats filters strip from forum layout. 2024-11-15 10:57:08 +03:00
23rd
49223a4688 Fixed possible crash for bot owners in earn section for bots. 2024-11-15 10:57:08 +03:00
ziplantil
074bb1e66e Fix bug in ChatBotCommands::update when commands empty (#28631) 2024-11-14 11:35:57 +04:00
174 changed files with 5696 additions and 960 deletions

View File

@@ -986,6 +986,10 @@ PRIVATE
info/profile/info_profile_values.h
info/profile/info_profile_widget.cpp
info/profile/info_profile_widget.h
info/reactions_list/info_reactions_list_widget.cpp
info/reactions_list/info_reactions_list_widget.h
info/requests_list/info_requests_list_widget.cpp
info/requests_list/info_requests_list_widget.h
info/saved/info_saved_sublists_widget.cpp
info/saved/info_saved_sublists_widget.h
info/settings/info_settings_widget.cpp
@@ -1036,6 +1040,10 @@ PRIVATE
info/info_wrap_widget.h
inline_bots/bot_attach_web_view.cpp
inline_bots/bot_attach_web_view.h
inline_bots/inline_bot_confirm_prepared.cpp
inline_bots/inline_bot_confirm_prepared.h
inline_bots/inline_bot_downloads.cpp
inline_bots/inline_bot_downloads.h
inline_bots/inline_bot_layout_internal.cpp
inline_bots/inline_bot_layout_internal.h
inline_bots/inline_bot_layout_item.cpp

Binary file not shown.

View File

@@ -682,6 +682,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_messages_privacy" = "Messages";
"lng_settings_voices_privacy" = "Voice messages";
"lng_settings_bio_privacy" = "Bio";
"lng_settings_gifts_privacy" = "Gifts";
"lng_settings_birthday_privacy" = "Date of Birth";
"lng_settings_privacy_premium" = "Only subscribers of {link} can restrict receiving voice messages.";
"lng_settings_privacy_premium_link" = "Telegram Premium";
@@ -1162,19 +1163,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_blocked_list_subtitle#other" = "{count} blocked users";
"lng_edit_privacy_everyone" = "Everybody";
"lng_edit_privacy_no_miniapps" = "Not Mini Apps";
"lng_edit_privacy_contacts" = "My contacts";
"lng_edit_privacy_close_friends" = "Close friends";
"lng_edit_privacy_contacts_and_premium" = "Contacts & Premium";
"lng_edit_privacy_contacts_and_miniapps" = "Contacts & Mini Apps";
"lng_edit_privacy_nobody" = "Nobody";
"lng_edit_privacy_premium" = "Premium users";
"lng_edit_privacy_miniapps" = "Mini Apps";
"lng_edit_privacy_exceptions" = "Add exceptions";
"lng_edit_privacy_user_types" = "User types";
"lng_edit_privacy_users_and_groups" = "Users and groups";
"lng_edit_privacy_premium_status" = "all Telegram Premium subscribers";
"lng_edit_privacy_miniapps_status" = "web mini apps that you use";
"lng_edit_privacy_exceptions_count#one" = "{count} user";
"lng_edit_privacy_exceptions_count#other" = "{count} users";
"lng_edit_privacy_exceptions_premium_and" = "Premium & {users}";
"lng_edit_privacy_exceptions_miniapps_and" = "Mini Apps & {users}";
"lng_edit_privacy_exceptions_add" = "Add users";
"lng_edit_privacy_phone_number_title" = "Phone number privacy";
@@ -1228,6 +1234,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_privacy_birthday_yet" = "You haven't entered your date of birth yet.\n{link}";
"lng_edit_privacy_birthday_yet_link" = "Add my birthday >";
"lng_edit_privacy_gifts_title" = "Gifts";
"lng_edit_privacy_gifts_header" = "Who can display gifts on my profile";
"lng_edit_privacy_gifts_always_empty" = "Always allow";
"lng_edit_privacy_gifts_never_empty" = "Never allow";
"lng_edit_privacy_gifts_exceptions" = "Choose whether gifts from specific senders need your approval before they're visible to others on your profile.";
"lng_edit_privacy_gifts_always_title" = "Always allow";
"lng_edit_privacy_gifts_never_title" = "Never allow";
"lng_edit_privacy_calls_title" = "Calls";
"lng_edit_privacy_calls_header" = "Who can call me";
"lng_edit_privacy_calls_always_empty" = "Always allow";
@@ -1461,11 +1475,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_open_app" = "Open App";
"lng_profile_open_app_about" = "By launching this mini app, you agree to the {terms}.";
"lng_profile_open_app_terms" = "Terms of Service for Mini Apps";
"lng_profile_bot_permissions_title" = "Allow access to";
"lng_profile_bot_emoji_status_access" = "Emoji Status";
"lng_info_add_as_contact" = "Add to contacts";
"lng_profile_shared_media" = "Shared media";
"lng_profile_suggest_photo" = "Suggest Profile Photo";
"lng_profile_suggest_photo_from_clipboard" = "Suggest From Clipboard";
"lng_profile_set_photo_for" = "Set Profile Photo";
"lng_profile_set_photo_for_from_clipboard" = "Set From Clipboard";
"lng_profile_photo_reset" = "Reset to Original";
"lng_profile_photo_from_clipboard" = "From clipboard";
"lng_profile_suggest_sure" = "You can suggest {user} to set this photo for their Telegram profile.";
"lng_profile_suggest_button" = "Suggest";
"lng_profile_set_personal_sure" = "Only you will see this photo and it will replace any photo {user} sets for themselves.";
@@ -1874,6 +1893,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_gift_got_stars_text#one" = "Display this gift on your page or convert it to **{count}** Star.";
"lng_action_gift_got_stars_text#other" = "Display this gift on your page or convert it to **{count}** Stars.";
"lng_action_gift_got_gift_text" = "You can keep this gift on your page.";
"lng_action_gift_can_remove_text" = "You can remove this gift from your page.";
"lng_action_gift_sent_subtitle" = "Gift for {user}";
"lng_action_gift_sent_text#one" = "{user} can display this gift on their page or convert it to {count} Star.";
"lng_action_gift_sent_text#other" = "{user} can display this gift on their page or convert it to {count} Stars.";
@@ -2413,6 +2433,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_credits_summary_history_tab_out" = "Outgoing";
"lng_credits_summary_history_entry_inner_in" = "In-App Purchase";
"lng_credits_summary_balance" = "Balance";
"lng_credits_more_options" = "More Options";
"lng_credits_balance_me" = "your balance";
"lng_credits_buy_button" = "Buy More Stars";
"lng_credits_gift_button" = "Gift Stars to Friends";
"lng_credits_box_out_title" = "Confirm Your Purchase";
"lng_credits_box_out_sure#one" = "Do you want to buy **\"{text}\"** in **{bot}** for **{count} Star**?";
@@ -3412,6 +3435,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_bot_add_to_side_menu_done" = "Bot added to the main menu.";
"lng_bot_no_scan_qr" = "QR Codes for bots are not supported on Desktop. Please use one of Telegram's mobile apps.";
"lng_bot_no_share_story" = "Sharing to Stories is not supported on Desktop. Please use one of Telegram's mobile apps.";
"lng_bot_emoji_status_confirm" = "Confirm";
"lng_bot_emoji_status_title" = "Set Emoji Status";
"lng_bot_emoji_status_text" = "Do you want to set this emoji status suggested by {bot}?";
"lng_bot_emoji_status_access_text" = "{bot} requests access to set your **emoji status**. You will be able to revoke this access in the profile page of {name}.";
"lng_bot_emoji_status_access_allow" = "Allow";
"lng_bot_share_prepared_title" = "Share Message";
"lng_bot_share_prepared_about" = "{bot} mini app suggests you to send this message to a chat you select.";
"lng_bot_share_prepared_button" = "Share With...";
"lng_bot_download_file" = "Download File";
"lng_bot_download_file_sure" = "{bot} suggests you download the following file:";
"lng_bot_download_file_button" = "Download";
"lng_bot_download_starting" = "Starting...";
"lng_bot_download_failed" = "Failed. {retry}";
"lng_bot_download_retry" = "Retry";
"lng_bot_status_users#one" = "{count} monthly user";
"lng_bot_status_users#other" = "{count} monthly users";
@@ -4356,7 +4393,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_edit_admin_rank_about" = "A title that members will see instead of '{title}'.";
"lng_rights_about_add_admins_yes" = "This admin will be able to add new admins with equal or fewer rights.";
"lng_rights_about_add_admins_no" = "This admin will not be able to add new admins.";
"lng_rights_about_by" = "This admin promoted by {user} at {date}.";
"lng_rights_about_by" = "This admin promoted by {user} on {date}.";
"lng_rights_about_admin_cant_edit" = "You can't edit the rights of this admin.";
"lng_rights_about_restriction_cant_edit" = "You cannot change the restrictions for this user.";
@@ -4435,8 +4472,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_chat_files" = "Files";
"lng_rights_chat_voice_messages" = "Voice messages";
"lng_rights_chat_video_messages" = "Video messages";
"lng_rights_chat_restricted_by" = "Restricted by {user} at {date}.";
"lng_rights_chat_banned_by" = "Banned by {user} at {date}.";
"lng_rights_chat_restricted_by" = "Restricted by {user} on {date}.";
"lng_rights_chat_banned_by" = "Banned by {user} on {date}.";
"lng_rights_chat_banned_until_header" = "Restricted until";
"lng_rights_chat_banned_forever" = "Forever";
"lng_rights_chat_banned_day#one" = "For {count} day";
@@ -5045,6 +5082,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_outdated_now" = "So Telegram Desktop can update to newer versions.";
"lng_filters_all" = "All chats";
"lng_filters_all_short" = "All";
"lng_filters_setup" = "Edit";
"lng_filters_title" = "Folders";
"lng_filters_subtitle" = "My folders";

View File

@@ -28,6 +28,7 @@
<file alias="collectible_phone.tgs">../../animations/collectible_phone.tgs</file>
<file alias="search.tgs">../../animations/search.tgs</file>
<file alias="noresults.tgs">../../animations/noresults.tgs</file>
<file alias="hello_status.tgs">../../animations/hello_status.tgs</file>
<file alias="dice_idle.tgs">../../animations/dice/dice_idle.tgs</file>
<file alias="dart_idle.tgs">../../animations/dice/dart_idle.tgs</file>

View File

@@ -10,7 +10,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="5.7.3.0" />
Version="5.8.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 5,7,3,0
PRODUCTVERSION 5,7,3,0
FILEVERSION 5,8,2,0
PRODUCTVERSION 5,8,2,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "5.7.3.0"
VALUE "FileVersion", "5.8.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "5.7.3.0"
VALUE "ProductVersion", "5.8.2.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,7,3,0
PRODUCTVERSION 5,7,3,0
FILEVERSION 5,8,2,0
PRODUCTVERSION 5,8,2,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "5.7.3.0"
VALUE "FileVersion", "5.8.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "5.7.3.0"
VALUE "ProductVersion", "5.8.2.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -117,11 +117,12 @@ constexpr auto kTransactionsLimit = 100;
? base::unixtime::parse(tl.data().vtransaction_date()->v)
: QDateTime(),
.successLink = qs(tl.data().vtransaction_url().value_or_empty()),
.convertStars = int(stargift
.starsConverted = int(stargift
? stargift->data().vconvert_stars().v
: 0),
.floodSkip = int(tl.data().vfloodskip_number().value_or(0)),
.converted = stargift && incoming,
.stargift = stargift.has_value(),
.reaction = tl.data().is_reaction(),
.refunded = tl.data().is_refund(),
.pending = tl.data().is_pending(),
@@ -174,7 +175,8 @@ constexpr auto kTransactionsLimit = 100;
.balance = status.data().vbalance().v,
.subscriptionsMissingBalance
= status.data().vsubscriptions_missing_balance().value_or_empty(),
.allLoaded = !status.data().vnext_offset().has_value(),
.allLoaded = !status.data().vnext_offset().has_value()
&& !status.data().vsubscriptions_next_offset().has_value(),
.token = qs(status.data().vnext_offset().value_or_empty()),
.tokenSubscriptions = qs(
status.data().vsubscriptions_next_offset().value_or_empty()),

View File

@@ -40,6 +40,7 @@ void HandleWithdrawalButton(
std::shared_ptr<Ui::Show> show) {
Expects(receiver.currencyReceiver
|| (receiver.creditsReceiver && receiver.creditsAmount));
struct State {
rpl::lifetime lifetime;
bool loading = false;
@@ -58,8 +59,7 @@ void HandleWithdrawalButton(
const auto processOut = [=] {
if (state->loading) {
return;
}
if (peer && !receiver.creditsAmount()) {
} else if (peer && !receiver.creditsAmount()) {
return;
}
state->loading = true;

View File

@@ -772,12 +772,13 @@ std::optional<StarGift> FromTL(
return StarGift{
.id = uint64(data.vid().v),
.stars = int64(data.vstars().v),
.convertStars = int64(data.vconvert_stars().v),
.starsConverted = int64(data.vconvert_stars().v),
.document = document,
.limitedLeft = remaining.value_or_empty(),
.limitedCount = total.value_or_empty(),
.firstSaleDate = data.vfirst_sale_date().value_or_empty(),
.lastSaleDate = data.vlast_sale_date().value_or_empty(),
.birthday = data.is_birthday(),
};
}
@@ -800,7 +801,7 @@ std::optional<UserStarGift> FromTL(
data.vmessage()->data().ventities().v),
}
: TextWithEntities()),
.convertStars = int64(data.vconvert_stars().value_or_empty()),
.starsConverted = int64(data.vconvert_stars().value_or_empty()),
.fromId = (data.vfrom_id()
? peerFromUser(data.vfrom_id()->v)
: PeerId()),

View File

@@ -76,12 +76,13 @@ struct GiftOptionData {
struct StarGift {
uint64 id = 0;
int64 stars = 0;
int64 convertStars = 0;
int64 starsConverted = 0;
not_null<DocumentData*> document;
int limitedLeft = 0;
int limitedCount = 0;
TimeId firstSaleDate = 0;
TimeId lastSaleDate = 0;
bool birthday = false;
friend inline bool operator==(
const StarGift &,
@@ -91,7 +92,7 @@ struct StarGift {
struct UserStarGift {
StarGift info;
TextWithEntities message;
int64 convertStars = 0;
int64 starsConverted = 0;
PeerId fromId = 0;
MsgId messageId = 0;
TimeId date = 0;

View File

@@ -69,6 +69,9 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
if (rule.always.premiums && (rule.option != Option::Everyone)) {
result.push_back(MTP_inputPrivacyValueAllowPremium());
}
if (rule.always.miniapps && (rule.option != Option::Everyone)) {
result.push_back(MTP_inputPrivacyValueAllowBots());
}
}
if (!rule.ignoreNever) {
const auto users = collectInputUsers(rule.never);
@@ -83,6 +86,9 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
MTP_inputPrivacyValueDisallowChatParticipants(
MTP_vector<MTPlong>(chats)));
}
if (rule.never.miniapps && (rule.option != Option::Nobody)) {
result.push_back(MTP_inputPrivacyValueDisallowBots());
}
}
result.push_back([&] {
switch (rule.option) {
@@ -124,6 +130,10 @@ UserPrivacy::Rule TLToRules(const TLRules &rules, Data::Session &owner) {
setOption(Option::CloseFriends);
}, [&](const MTPDprivacyValueAllowPremium &) {
result.always.premiums = true;
}, [&](const MTPDprivacyValueAllowBots &) {
result.always.miniapps = true;
}, [&](const MTPDprivacyValueDisallowBots &) {
result.never.miniapps = true;
}, [&](const MTPDprivacyValueAllowUsers &data) {
const auto &users = data.vusers().v;
always.reserve(always.size() + users.size());
@@ -199,6 +209,7 @@ MTPInputPrivacyKey KeyToTL(UserPrivacy::Key key) {
case Key::Voices: return MTP_inputPrivacyKeyVoiceMessages();
case Key::About: return MTP_inputPrivacyKeyAbout();
case Key::Birthday: return MTP_inputPrivacyKeyBirthday();
case Key::GiftsAutoSave: return MTP_inputPrivacyKeyStarGiftsAutoSave();
}
Unexpected("Key in Api::UserPrivacy::KetToTL.");
}
@@ -228,6 +239,8 @@ std::optional<UserPrivacy::Key> TLToKey(mtpTypeId type) {
case mtpc_inputPrivacyKeyAbout: return Key::About;
case mtpc_privacyKeyBirthday:
case mtpc_inputPrivacyKeyBirthday: return Key::Birthday;
case mtpc_privacyKeyStarGiftsAutoSave:
case mtpc_inputPrivacyKeyStarGiftsAutoSave: return Key::GiftsAutoSave;
}
return std::nullopt;
}

View File

@@ -31,6 +31,7 @@ public:
Voices,
About,
Birthday,
GiftsAutoSave,
};
enum class Option {
Everyone,
@@ -41,6 +42,7 @@ public:
struct Exceptions {
std::vector<not_null<PeerData*>> peers;
bool premiums = false;
bool miniapps = false;
};
struct Rule {
Option option = Option::Everyone;

View File

@@ -3947,7 +3947,8 @@ void ApiWrap::sendInlineResult(
not_null<UserData*> bot,
not_null<InlineBots::Result*> data,
const SendAction &action,
std::optional<MsgId> localMessageId) {
std::optional<MsgId> localMessageId,
Fn<void(bool)> done) {
sendAction(action);
const auto history = action.history;
@@ -4027,11 +4028,17 @@ void ApiWrap::sendInlineResult(
history->finishSavingCloudDraft(
topicRootId,
UnixtimeFromMsgId(response.outerMsgId));
if (done) {
done(true);
}
}, [=](const MTP::Error &error, const MTP::Response &response) {
sendMessageFail(error, peer, randomId, newId);
history->finishSavingCloudDraft(
topicRootId,
UnixtimeFromMsgId(response.outerMsgId));
if (done) {
done(false);
}
});
finishForwarding(action);
}

View File

@@ -361,7 +361,8 @@ public:
not_null<UserData*> bot,
not_null<InlineBots::Result*> data,
const SendAction &action,
std::optional<MsgId> localMessageId);
std::optional<MsgId> localMessageId,
Fn<void(bool)> done = nullptr);
void sendMessageFail(
const MTP::Error &error,
not_null<PeerData*> peer,

View File

@@ -96,6 +96,9 @@ void ChangeFilterById(
Ui::Text::WithEntities));
}
}).fail([=](const MTP::Error &error) {
LOG(("API Error: failed to %1 a dialog to a folder. %2")
.arg(add ? u"add"_q : u"remove"_q)
.arg(error.type()));
// Revert filter on fail.
history->owner().chatsFilters().set(was);
}).send();

View File

@@ -36,13 +36,63 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_settings.h"
#include "styles/style_layers.h"
#include "styles/style_menu_icons.h"
#include "styles/style_window.h"
namespace {
constexpr auto kPremiumsRowId = PeerId(FakeChatId(BareId(1))).value;
constexpr auto kMiniAppsRowId = PeerId(FakeChatId(BareId(2))).value;
using Exceptions = Api::UserPrivacy::Exceptions;
enum class SpecialRowType {
Premiums,
MiniApps,
};
[[nodiscard]] PaintRoundImageCallback GeneratePremiumsUserpicCallback(
bool forceRound) {
return [=](QPainter &p, int x, int y, int outerWidth, int size) {
auto gradient = QLinearGradient(
QPointF(x, y),
QPointF(x + size, y + size));
gradient.setStops(Ui::Premium::ButtonGradientStops());
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(gradient);
if (forceRound) {
p.drawEllipse(x, y, size, size);
} else {
const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
p.drawRoundedRect(x, y, size, size, radius, radius);
}
st::settingsPrivacyPremium.paintInCenter(p, QRect(x, y, size, size));
};
}
[[nodiscard]] PaintRoundImageCallback GenerateMiniAppsUserpicCallback(
bool forceRound) {
return [=](QPainter &p, int x, int y, int outerWidth, int size) {
const auto &color1 = st::historyPeer6UserpicBg;
const auto &color2 = st::historyPeer6UserpicBg2;
auto hq = PainterHighQualityEnabler(p);
auto gradient = QLinearGradient(x, y, x, y + size);
gradient.setStops({ { 0., color1->c }, { 1., color2->c } });
p.setPen(Qt::NoPen);
p.setBrush(gradient);
if (forceRound) {
p.drawEllipse(x, y, size, size);
} else {
const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
p.drawRoundedRect(x, y, size, size, radius, radius);
}
st::windowFilterTypeBots.paintInCenter(p, QRect(x, y, size, size));
};
}
void CreateRadiobuttonLock(
not_null<Ui::RpWidget*> widget,
const style::Checkbox &st) {
@@ -102,7 +152,7 @@ public:
not_null<Main::Session*> session,
rpl::producer<QString> title,
const Exceptions &selected,
bool allowChoosePremiums);
std::optional<SpecialRowType> allowChooseSpecial);
Main::Session &session() const override;
void rowClicked(not_null<PeerListRow*> row) override;
@@ -110,18 +160,20 @@ public:
bool handleDeselectForeignRow(PeerListRowId itemId) override;
[[nodiscard]] bool premiumsSelected() const;
[[nodiscard]] bool miniAppsSelected() const;
protected:
void prepareViewHook() override;
std::unique_ptr<Row> createRow(not_null<History*> history) override;
private:
[[nodiscard]] object_ptr<Ui::RpWidget> preparePremiumsRowList();
[[nodiscard]] object_ptr<Ui::RpWidget> prepareSpecialRowList(
SpecialRowType type);
const not_null<Main::Session*> _session;
rpl::producer<QString> _title;
Exceptions _selected;
bool _allowChoosePremiums = false;
std::optional<SpecialRowType> _allowChooseSpecial;
PeerListContentDelegate *_typesDelegate = nullptr;
Fn<void(PeerListRowId)> _deselectOption;
@@ -133,9 +185,9 @@ struct RowSelectionChange {
bool checked = false;
};
class PremiumsRow final : public PeerListRow {
class SpecialRow final : public PeerListRow {
public:
PremiumsRow();
explicit SpecialRow(SpecialRowType type);
QString generateName() override;
QString generateShortName() override;
@@ -147,66 +199,61 @@ public:
class TypesController final : public PeerListController {
public:
TypesController(not_null<Main::Session*> session, bool premiums);
TypesController(not_null<Main::Session*> session, SpecialRowType type);
Main::Session &session() const override;
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
[[nodiscard]] bool premiumsSelected() const;
[[nodiscard]] rpl::producer<bool> premiumsChanges() const;
[[nodiscard]] bool specialSelected() const;
[[nodiscard]] rpl::producer<bool> specialChanges() const;
[[nodiscard]] auto rowSelectionChanges() const
-> rpl::producer<RowSelectionChange>;
private:
const not_null<Main::Session*> _session;
const SpecialRowType _type;
rpl::event_stream<> _selectionChanged;
rpl::event_stream<RowSelectionChange> _rowSelectionChanges;
};
PremiumsRow::PremiumsRow() : PeerListRow(kPremiumsRowId) {
setCustomStatus(tr::lng_edit_privacy_premium_status(tr::now));
SpecialRow::SpecialRow(SpecialRowType type)
: PeerListRow((type == SpecialRowType::Premiums)
? kPremiumsRowId
: kMiniAppsRowId) {
setCustomStatus((id() == kPremiumsRowId)
? tr::lng_edit_privacy_premium_status(tr::now)
: tr::lng_edit_privacy_miniapps_status(tr::now));
}
QString PremiumsRow::generateName() {
return tr::lng_edit_privacy_premium(tr::now);
QString SpecialRow::generateName() {
return (id() == kPremiumsRowId)
? tr::lng_edit_privacy_premium(tr::now)
: tr::lng_edit_privacy_miniapps(tr::now);
}
QString PremiumsRow::generateShortName() {
QString SpecialRow::generateShortName() {
return generateName();
}
PaintRoundImageCallback PremiumsRow::generatePaintUserpicCallback(
PaintRoundImageCallback SpecialRow::generatePaintUserpicCallback(
bool forceRound) {
return [=](QPainter &p, int x, int y, int outerWidth, int size) {
auto gradient = QLinearGradient(
QPointF(x, y),
QPointF(x + size, y + size));
gradient.setStops(Ui::Premium::ButtonGradientStops());
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(gradient);
if (forceRound) {
p.drawEllipse(x, y, size, size);
} else {
const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
p.drawRoundedRect(x, y, size, size, radius, radius);
}
st::settingsPrivacyPremium.paintInCenter(p, QRect(x, y, size, size));
};
return (id() == kPremiumsRowId)
? GeneratePremiumsUserpicCallback(forceRound)
: GenerateMiniAppsUserpicCallback(forceRound);
}
bool PremiumsRow::useForumLikeUserpic() const {
bool SpecialRow::useForumLikeUserpic() const {
return true;
}
TypesController::TypesController(
not_null<Main::Session*> session,
bool premiums)
: _session(session) {
SpecialRowType type)
: _session(session)
, _type(type) {
}
Main::Session &TypesController::session() const {
@@ -214,12 +261,15 @@ Main::Session &TypesController::session() const {
}
void TypesController::prepare() {
delegate()->peerListAppendRow(std::make_unique<PremiumsRow>());
delegate()->peerListAppendRow(std::make_unique<SpecialRow>(_type));
delegate()->peerListRefreshRows();
}
bool TypesController::premiumsSelected() const {
const auto row = delegate()->peerListFindRow(kPremiumsRowId);
bool TypesController::specialSelected() const {
const auto premiums = (_type == SpecialRowType::Premiums);
const auto row = delegate()->peerListFindRow(premiums
? kPremiumsRowId
: kMiniAppsRowId);
Assert(row != nullptr);
return row->checked();
@@ -231,10 +281,10 @@ void TypesController::rowClicked(not_null<PeerListRow*> row) {
_rowSelectionChanges.fire({ row, checked });
}
rpl::producer<bool> TypesController::premiumsChanges() const {
rpl::producer<bool> TypesController::specialChanges() const {
return _rowSelectionChanges.events(
) | rpl::map([=] {
return premiumsSelected();
return specialSelected();
});
}
@@ -247,12 +297,12 @@ PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(
not_null<Main::Session*> session,
rpl::producer<QString> title,
const Exceptions &selected,
bool allowChoosePremiums)
std::optional<SpecialRowType> allowChooseSpecial)
: ChatsListBoxController(session)
, _session(session)
, _title(std::move(title))
, _selected(selected)
, _allowChoosePremiums(allowChoosePremiums) {
, _allowChooseSpecial(allowChooseSpecial) {
}
Main::Session &PrivacyExceptionsBoxController::session() const {
@@ -261,14 +311,18 @@ Main::Session &PrivacyExceptionsBoxController::session() const {
void PrivacyExceptionsBoxController::prepareViewHook() {
delegate()->peerListSetTitle(std::move(_title));
if (_allowChoosePremiums || _selected.premiums) {
delegate()->peerListSetAboveWidget(preparePremiumsRowList());
if (_allowChooseSpecial || _selected.premiums || _selected.miniapps) {
delegate()->peerListSetAboveWidget(prepareSpecialRowList(
_allowChooseSpecial.value_or(_selected.premiums
? SpecialRowType::Premiums
: SpecialRowType::MiniApps)));
}
delegate()->peerListAddSelectedPeers(_selected.peers);
}
bool PrivacyExceptionsBoxController::isForeignRow(PeerListRowId itemId) {
return (itemId == kPremiumsRowId);
return (itemId == kPremiumsRowId)
|| (itemId == kMiniAppsRowId);
}
bool PrivacyExceptionsBoxController::handleDeselectForeignRow(
@@ -280,7 +334,8 @@ bool PrivacyExceptionsBoxController::handleDeselectForeignRow(
return false;
}
auto PrivacyExceptionsBoxController::preparePremiumsRowList()
auto PrivacyExceptionsBoxController::prepareSpecialRowList(
SpecialRowType type)
-> object_ptr<Ui::RpWidget> {
auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
const auto container = result.data();
@@ -291,30 +346,39 @@ auto PrivacyExceptionsBoxController::preparePremiumsRowList()
_typesDelegate = lifetime.make_state<PeerListContentDelegateSimple>();
const auto controller = lifetime.make_state<TypesController>(
&session(),
_selected.premiums);
type);
const auto content = result->add(object_ptr<PeerListContent>(
container,
controller));
_typesDelegate->setContent(content);
controller->setDelegate(_typesDelegate);
const auto selectType = [&](PeerListRowId id) {
const auto row = _typesDelegate->peerListFindRow(id);
if (row) {
content->changeCheckState(row, true, anim::type::instant);
this->delegate()->peerListSetForeignRowChecked(
row,
true,
anim::type::instant);
}
};
if (_selected.premiums) {
const auto row = _typesDelegate->peerListFindRow(kPremiumsRowId);
Assert(row != nullptr);
content->changeCheckState(row, true, anim::type::instant);
this->delegate()->peerListSetForeignRowChecked(
row,
true,
anim::type::instant);
selectType(kPremiumsRowId);
} else if (_selected.miniapps) {
selectType(kMiniAppsRowId);
}
container->add(CreatePeerListSectionSubtitle(
container,
tr::lng_edit_privacy_users_and_groups()));
controller->premiumsChanges(
) | rpl::start_with_next([=](bool premiums) {
_selected.premiums = premiums;
controller->specialChanges(
) | rpl::start_with_next([=](bool chosen) {
if (type == SpecialRowType::Premiums) {
_selected.premiums = chosen;
} else {
_selected.miniapps = chosen;
}
}, lifetime);
controller->rowSelectionChanges(
@@ -329,6 +393,8 @@ auto PrivacyExceptionsBoxController::preparePremiumsRowList()
if (const auto row = _typesDelegate->peerListFindRow(itemId)) {
if (itemId == kPremiumsRowId) {
_selected.premiums = false;
} else if (itemId == kMiniAppsRowId) {
_selected.miniapps = false;
}
_typesDelegate->peerListSetRowChecked(row, false);
}
@@ -337,10 +403,14 @@ auto PrivacyExceptionsBoxController::preparePremiumsRowList()
return result;
}
[[nodiscard]] bool PrivacyExceptionsBoxController::premiumsSelected() const {
bool PrivacyExceptionsBoxController::premiumsSelected() const {
return _selected.premiums;
}
bool PrivacyExceptionsBoxController::miniAppsSelected() const {
return _selected.miniapps;
}
void PrivacyExceptionsBoxController::rowClicked(not_null<PeerListRow*> row) {
const auto peer = row->peer();
@@ -412,6 +482,11 @@ EditPrivacyBox::EditPrivacyBox(
// If we switch from Everyone to Contacts or Nobody suggest Premiums.
_value.always.premiums = true;
}
if (_controller->allowMiniAppsToggle(Exception::Always)
&& _value.option == Option::Everyone) {
// If we switch from Everyone to Contacts or Nobody suggest MiniApps.
_value.always.miniapps = true;
}
}
void EditPrivacyBox::prepare() {
@@ -427,12 +502,18 @@ void EditPrivacyBox::editExceptions(
&_window->session(),
_controller->exceptionBoxTitle(exception),
exceptions(exception),
_controller->allowPremiumsToggle(exception));
(_controller->allowPremiumsToggle(exception)
? SpecialRowType::Premiums
: _controller->allowMiniAppsToggle(exception)
? SpecialRowType::MiniApps
: std::optional<SpecialRowType>()));
auto initBox = [=, controller = controller.get()](
not_null<PeerListBox*> box) {
box->addButton(tr::lng_settings_save(), crl::guard(this, [=] {
exceptions(exception).peers = box->collectSelectedRows();
exceptions(exception).premiums = controller->premiumsSelected();
auto &setTo = exceptions(exception);
setTo.peers = box->collectSelectedRows();
setTo.premiums = controller->premiumsSelected();
setTo.miniapps = controller->miniAppsSelected();
const auto type = [&] {
switch (exception) {
case Exception::Always: return Exception::Never;
@@ -440,11 +521,17 @@ void EditPrivacyBox::editExceptions(
}
Unexpected("Invalid exception value.");
}();
auto &removeFrom = exceptions(type).peers;
auto &removeFrom = exceptions(type);
for (const auto peer : exceptions(exception).peers) {
removeFrom.erase(
ranges::remove(removeFrom, peer),
end(removeFrom));
removeFrom.peers.erase(
ranges::remove(removeFrom.peers, peer),
end(removeFrom.peers));
}
if (setTo.premiums) {
removeFrom.premiums = false;
}
if (setTo.miniapps) {
removeFrom.miniapps = false;
}
done();
box->closeBox();
@@ -566,14 +653,21 @@ void EditPrivacyBox::setupContent() {
lt_count,
count)
: tr::lng_edit_privacy_exceptions_add(tr::now);
return !value.premiums
? users
: !count
? tr::lng_edit_privacy_premium(tr::now)
: tr::lng_edit_privacy_exceptions_premium_and(
tr::now,
lt_users,
users);
return value.premiums
? (!count
? tr::lng_edit_privacy_premium(tr::now)
: tr::lng_edit_privacy_exceptions_premium_and(
tr::now,
lt_users,
users))
: value.miniapps
? (!count
? tr::lng_edit_privacy_miniapps(tr::now)
: tr::lng_edit_privacy_exceptions_miniapps_and(
tr::now,
lt_users,
users))
: users;
});
_controller->handleExceptionsChange(
exception,

View File

@@ -61,6 +61,10 @@ public:
Exception exception) const {
return false;
}
[[nodiscard]] virtual bool allowMiniAppsToggle(
Exception exception) const {
return false;
}
virtual void handleExceptionsChange(
Exception exception,
rpl::producer<int> value) {

View File

@@ -123,7 +123,9 @@ void GiftCreditsBox(
box->verticalLayout(),
peer,
0,
[=] { gifted(); box->uiShow()->hideLayer(); });
[=] { gifted(); box->uiShow()->hideLayer(); },
tr::lng_credits_summary_options_subtitle(),
{});
box->setPinnedToBottomContent(
object_ptr<Ui::VerticalLayout>(box));

View File

@@ -266,7 +266,7 @@ object_ptr<Ui::RpWidget> MakeStarGiftStarsValue(
raw,
tr::lng_gift_sell_small(
lt_count_decimal,
rpl::single(entry.convertStars * 1.)),
rpl::single(entry.starsConverted * 1.)),
st::starGiftSmallButton)
: nullptr;
if (convert) {
@@ -1044,7 +1044,8 @@ void AddStarGiftTable(
const auto peerId = PeerId(entry.barePeerId);
const auto session = &controller->session();
if (peerId) {
const auto withSendButton = entry.in;
const auto user = session->data().peer(peerId)->asUser();
const auto withSendButton = entry.in && user && !user->isBot();
AddTableRow(
table,
tr::lng_credits_box_history_entry_peer_in(),
@@ -1144,12 +1145,17 @@ void AddCreditsHistoryEntryTable(
st::giveawayGiftCodeTable),
st::giveawayGiftCodeTableMargin);
const auto peerId = PeerId(entry.barePeerId);
const auto actorId = PeerId(entry.bareActorId);
const auto session = &controller->session();
if (peerId) {
if (actorId || peerId) {
auto text = entry.in
? tr::lng_credits_box_history_entry_peer_in()
: tr::lng_credits_box_history_entry_peer();
AddTableRow(table, std::move(text), controller, peerId);
AddTableRow(
table,
std::move(text),
controller,
actorId ? actorId : peerId);
}
if (const auto msgId = MsgId(peerId ? entry.bareMsgId : 0)) {
const auto peer = session->data().peer(peerId);
@@ -1242,12 +1248,24 @@ void AddCreditsHistoryEntryTable(
}
}
if (!entry.id.isEmpty()) {
constexpr auto kOneLineCount = 18;
const auto oneLine = entry.id.length() <= kOneLineCount;
constexpr auto kOneLineCount = 22;
const auto oneLine = entry.id.size() <= kOneLineCount;
auto multiLine = QString();
if (!oneLine) {
for (auto i = 0; i < entry.id.size(); ++i) {
multiLine.append(entry.id[i]);
if ((i + 1) % kOneLineCount == 0) {
multiLine.append('\n');
}
}
}
auto label = object_ptr<Ui::FlatLabel>(
table,
rpl::single(
Ui::Text::Wrapped({ entry.id }, EntityType::Code, {})),
Ui::Text::Wrapped(
{ oneLine ? entry.id : std::move(multiLine) },
EntityType::Code,
{})),
oneLine
? st::giveawayGiftCodeValue
: st::giveawayGiftCodeValueMultiline);

View File

@@ -240,7 +240,8 @@ int LocalStorageBox::Row::resizeGetHeight(int newWidth) {
}
void LocalStorageBox::Row::paintEvent(QPaintEvent *e) {
if (!_progress || true) {
#if 0 // not used
if (!_progress) {
return;
}
auto p = QPainter(this);
@@ -254,6 +255,7 @@ void LocalStorageBox::Row::paintEvent(QPaintEvent *e) {
st::proxyCheckingPosition.y() + bottom
},
width());
#endif
}
QString LocalStorageBox::Row::titleText(const Database::TaggedSummary &data) const {

View File

@@ -219,12 +219,15 @@ void PeerListBox::searchQueryChanged(const QString &query) {
scrollToY(0);
const auto isEmpty = content()->searchQueryChanged(query);
if (_specialTabsMode.enabled) {
const auto was = _specialTabsMode.searchIsActive;
_specialTabsMode.searchIsActive = !isEmpty;
if (_specialTabsMode.searchIsActive) {
_specialTabsMode.topSkip = _addedTopScrollSkip;
setAddedTopScrollSkip(0);
} else {
setAddedTopScrollSkip(_specialTabsMode.topSkip);
if (was != _specialTabsMode.searchIsActive) {
if (_specialTabsMode.searchIsActive) {
_specialTabsMode.topSkip = _addedTopScrollSkip;
setAddedTopScrollSkip(0);
} else {
setAddedTopScrollSkip(_specialTabsMode.topSkip);
}
}
}
}

View File

@@ -1065,6 +1065,11 @@ std::unique_ptr<PeerListRow> ChooseTopicBoxController::createSearchRow(
return nullptr;
}
std::unique_ptr<PeerListRow> ChooseTopicBoxController::MakeRow(
not_null<Data::ForumTopic*> topic) {
return std::make_unique<Row>(topic);
}
auto ChooseTopicBoxController::createRow(not_null<Data::ForumTopic*> topic)
-> std::unique_ptr<Row> {
const auto skip = _filter && !_filter(topic);

View File

@@ -335,6 +335,9 @@ public:
void loadMoreRows() override;
std::unique_ptr<PeerListRow> createSearchRow(PeerListRowId id) override;
[[nodiscard]] static std::unique_ptr<PeerListRow> MakeRow(
not_null<Data::ForumTopic*> topic);
private:
class Row final : public PeerListRow {
public:

View File

@@ -30,10 +30,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h"
#include "base/unixtime.h"
#include "ui/effects/outline_segments.h"
#include "ui/widgets/menu/menu_multiline_action.h"
#include "ui/widgets/popup_menu.h"
#include "ui/text/text_utilities.h"
#include "info/profile/info_profile_values.h"
#include "window/window_session_controller.h"
#include "history/history.h"
#include "styles/style_chat.h"
#include "styles/style_menu_icons.h"
namespace {
@@ -1645,6 +1648,51 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
auto result = base::make_unique_q<Ui::PopupMenu>(
parent,
st::popupMenuWithIcons);
const auto addToEnd = gsl::finally([&] {
const auto addInfoAction = [&](
not_null<PeerData*> by,
tr::phrase<lngtag_user, lngtag_date> phrase,
TimeId since) {
auto text = phrase(
tr::now,
lt_user,
Ui::Text::Bold(by->name()),
lt_date,
Ui::Text::Bold(
langDateTimeFull(base::unixtime::parse(since))),
Ui::Text::WithEntities);
auto button = base::make_unique_q<Ui::Menu::MultilineAction>(
result->menu(),
result->st().menu,
st::historyHasCustomEmoji,
st::historyHasCustomEmojiPosition,
std::move(text));
if (const auto n = _navigation) {
button->setClickedCallback([=] {
n->parentController()->show(PrepareShortInfoBox(by, n));
});
}
result->addSeparator();
result->addAction(std::move(button));
};
if (const auto by = _additional.restrictedBy(participant)) {
if (const auto since = _additional.restrictedSince(participant)) {
addInfoAction(
by,
_additional.isKicked(participant)
? tr::lng_rights_chat_banned_by
: tr::lng_rights_chat_restricted_by,
since);
}
} else if (user) {
if (const auto by = _additional.adminPromotedBy(user)) {
if (const auto since = _additional.adminPromotedSince(user)) {
addInfoAction(by, tr::lng_rights_about_by, since);
}
}
}
});
if (_navigation) {
result->addAction(
(participant->isUser()
@@ -1652,39 +1700,14 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
: participant->isBroadcast()
? tr::lng_context_view_channel
: tr::lng_context_view_group)(tr::now),
crl::guard(this, [=] {
_navigation->showPeerInfo(participant); }),
crl::guard(this, [=, this] {
_navigation->parentController()->show(
PrepareShortInfoBox(participant, _navigation));
}),
(participant->isUser()
? &st::menuIconProfile
: &st::menuIconInfo));
}
if (const auto by = _additional.restrictedBy(participant)) {
result->addAction(
(_role == Role::Kicked
? tr::lng_channel_banned_status_removed_by
: tr::lng_channel_banned_status_restricted_by)(
tr::now,
lt_user,
by->name()),
crl::guard(this, [=] {
_navigation->parentController()->show(
PrepareShortInfoBox(by, _navigation));
}),
&st::menuIconAdmin);
} else if (user) {
if (const auto by = _additional.adminPromotedBy(user)) {
result->addAction(
tr::lng_channel_admin_status_promoted_by(
tr::now,
lt_user,
by->name()),
crl::guard(this, [=] {
_navigation->parentController()->show(
PrepareShortInfoBox(by, _navigation));
}),
&st::menuIconAdmin);
}
}
if (_role == Role::Kicked) {
if (_peer->isMegagroup()
&& _additional.canRestrictParticipant(participant)) {

View File

@@ -12,12 +12,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "boxes/gift_premium_box.h"
#include "boxes/peer_list_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/share_box.h"
#include "core/application.h"
#include "core/ui_integration.h" // Core::MarkedTextContext.
#include "data/components/credits.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
#include "data/data_forum_topic.h"
#include "data/data_histories.h"
#include "data/data_peer.h"
#include "data/data_session.h"
@@ -51,6 +53,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_credits.h"
#include "styles/style_dialogs.h"
#include "styles/style_giveaway.h"
#include "styles/style_info.h"
#include "styles/style_layers.h" // st::boxDividerLabel.
@@ -264,8 +267,9 @@ private:
class SingleRowController final : public PeerListController {
public:
SingleRowController(
not_null<PeerData*> peer,
rpl::producer<QString> status);
not_null<Data::Thread*> thread,
rpl::producer<QString> status,
Fn<void()> clicked);
void prepare() override;
void loadMoreRows() override;
@@ -273,8 +277,10 @@ public:
Main::Session &session() const override;
private:
const not_null<PeerData*> _peer;
const not_null<Main::Session*> _session;
const base::weak_ptr<Data::Thread> _thread;
rpl::producer<QString> _status;
Fn<void()> _clicked;
rpl::lifetime _lifetime;
};
@@ -1144,36 +1150,59 @@ int Controller::descriptionTopSkipMin() const {
}
SingleRowController::SingleRowController(
not_null<PeerData*> peer,
rpl::producer<QString> status)
: _peer(peer)
, _status(std::move(status)) {
not_null<Data::Thread*> thread,
rpl::producer<QString> status,
Fn<void()> clicked)
: _session(&thread->session())
, _thread(thread)
, _status(std::move(status))
, _clicked(std::move(clicked)) {
}
void SingleRowController::prepare() {
auto row = std::make_unique<PeerListRow>(_peer);
const auto strong = _thread.get();
if (!strong) {
return;
}
const auto topic = strong->asTopic();
auto row = topic
? ChooseTopicBoxController::MakeRow(topic)
: std::make_unique<PeerListRow>(strong->peer());
const auto raw = row.get();
std::move(
_status
) | rpl::start_with_next([=](const QString &status) {
raw->setCustomStatus(status);
delegate()->peerListUpdateRow(raw);
}, _lifetime);
if (_status) {
std::move(
_status
) | rpl::start_with_next([=](const QString &status) {
raw->setCustomStatus(status);
delegate()->peerListUpdateRow(raw);
}, _lifetime);
}
delegate()->peerListAppendRow(std::move(row));
delegate()->peerListRefreshRows();
if (topic) {
topic->destroyed() | rpl::start_with_next([=] {
while (delegate()->peerListFullRowsCount()) {
delegate()->peerListRemoveRow(delegate()->peerListRowAt(0));
}
delegate()->peerListRefreshRows();
}, _lifetime);
}
}
void SingleRowController::loadMoreRows() {
}
void SingleRowController::rowClicked(not_null<PeerListRow*> row) {
ShowPeerInfoSync(row->peer());
if (const auto onstack = _clicked) {
onstack();
} else {
ShowPeerInfoSync(row->peer());
}
}
Main::Session &SingleRowController::session() const {
return _peer->session();
return *_session;
}
} // namespace
@@ -1186,14 +1215,29 @@ bool IsExpiredLink(const Api::InviteLink &data, TimeId now) {
void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container,
not_null<PeerData*> peer,
rpl::producer<QString> status) {
rpl::producer<QString> status,
Fn<void()> clicked) {
AddSinglePeerRow(
container,
peer->owner().history(peer),
std::move(status),
std::move(clicked));
}
void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container,
not_null<Data::Thread*> thread,
rpl::producer<QString> status,
Fn<void()> clicked) {
const auto delegate = container->lifetime().make_state<
PeerListContentDelegateSimple
>();
const auto controller = container->lifetime().make_state<
SingleRowController
>(peer, std::move(status));
controller->setStyleOverrides(&st::peerListSingleRow);
>(thread, std::move(status), std::move(clicked));
controller->setStyleOverrides(thread->asTopic()
? &st::chooseTopicList
: &st::peerListSingleRow);
const auto content = container->add(object_ptr<PeerListContent>(
container,
controller));

View File

@@ -16,6 +16,10 @@ namespace Api {
struct InviteLink;
} // namespace Api
namespace Data {
class Thread;
} // namespace Data
namespace Main {
class Session;
} // namespace Main
@@ -31,7 +35,14 @@ class BoxContent;
void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container,
not_null<PeerData*> peer,
rpl::producer<QString> status);
rpl::producer<QString> status,
Fn<void()> clicked = nullptr);
void AddSinglePeerRow(
not_null<Ui::VerticalLayout*> container,
not_null<Data::Thread*> thread,
rpl::producer<QString> status,
Fn<void()> clicked = nullptr);
void AddPermanentLinkBlock(
std::shared_ptr<Ui::Show> show,

View File

@@ -7,27 +7,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/peers/edit_peer_requests_box.h"
#include "ui/effects/ripple_animation.h"
#include "api/api_invite_links.h"
#include "apiwrap.h"
#include "base/unixtime.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/peers/edit_participants_box.h" // SubscribeToMigration
#include "boxes/peers/edit_peer_invite_link.h" // PrepareRequestedRowStatus
#include "boxes/peers/prepare_short_info_box.h" // PrepareShortInfoBox
#include "history/view/history_view_requests_bar.h" // kRecentRequestsLimit
#include "data/data_peer.h"
#include "data/data_user.h"
#include "data/data_chat.h"
#include "boxes/peers/edit_peer_requests_box.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "base/unixtime.h"
#include "data/data_user.h"
#include "history/view/history_view_requests_bar.h" // kRecentRequestsLimit
#include "info/info_controller.h"
#include "info/info_memento.h"
#include "info/requests_list/info_requests_list_widget.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "mtproto/sender.h"
#include "ui/effects/ripple_animation.h"
#include "ui/painter.h"
#include "ui/round_rect.h"
#include "ui/text/text_utilities.h"
#include "ui/painter.h"
#include "lang/lang_keys.h"
#include "window/window_session_controller.h"
#include "apiwrap.h"
#include "api/api_invite_links.h"
#include "styles/style_boxes.h"
namespace {
@@ -262,14 +265,10 @@ RequestsBoxController::~RequestsBoxController() = default;
void RequestsBoxController::Start(
not_null<Window::SessionNavigation*> navigation,
not_null<PeerData*> peer) {
auto controller = std::make_unique<RequestsBoxController>(
navigation,
peer->migrateToOrMe());
const auto initBox = [=](not_null<PeerListBox*> box) {
box->addButton(tr::lng_close(), [=] { box->closeBox(); });
};
navigation->parentController()->show(
Box<PeerListBox>(std::move(controller), initBox));
navigation->showSection(
std::make_shared<Info::Memento>(
peer->migrateToOrMe(),
Info::Section::Type::RequestsList));
}
Main::Session &RequestsBoxController::session() const {
@@ -289,6 +288,58 @@ std::unique_ptr<PeerListRow> RequestsBoxController::createSearchRow(
return nullptr;
}
std::unique_ptr<PeerListRow> RequestsBoxController::createRestoredRow(
not_null<PeerData*> peer) {
if (const auto user = peer->asUser()) {
return createRow(user, _dates[user]);
}
return nullptr;
}
auto RequestsBoxController::saveState() const
-> std::unique_ptr<PeerListState> {
auto result = PeerListController::saveState();
auto my = std::make_unique<SavedState>();
my->dates = _dates;
my->offsetDate = _offsetDate;
my->offsetUser = _offsetUser;
my->allLoaded = _allLoaded;
my->wasLoading = (_loadRequestId != 0);
if (const auto search = searchController()) {
my->searchState = search->saveState();
}
result->controllerState = std::move(my);
return result;
}
void RequestsBoxController::restoreState(
std::unique_ptr<PeerListState> state) {
auto typeErasedState = state
? state->controllerState.get()
: nullptr;
if (const auto my = dynamic_cast<SavedState*>(typeErasedState)) {
if (const auto requestId = base::take(_loadRequestId)) {
_api.request(requestId).cancel();
}
_dates = std::move(my->dates);
_offsetDate = my->offsetDate;
_offsetUser = my->offsetUser;
_allLoaded = my->allLoaded;
if (const auto search = searchController()) {
search->restoreState(std::move(my->searchState));
}
if (my->wasLoading) {
loadMoreRows();
}
PeerListController::restoreState(std::move(state));
if (delegate()->peerListFullRowsCount() || _allLoaded) {
refreshDescription();
delegate()->peerListRefreshRows();
}
}
}
void RequestsBoxController::prepare() {
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
delegate()->peerListSetTitle(_peer->isBroadcast()
@@ -356,9 +407,7 @@ void RequestsBoxController::refreshDescription() {
}
void RequestsBoxController::rowClicked(not_null<PeerListRow*> row) {
_navigation->parentController()->show(PrepareShortInfoBox(
row->peer(),
_navigation));
_navigation->showPeerInfo(row->peer());
}
void RequestsBoxController::rowElementClicked(
@@ -405,6 +454,7 @@ void RequestsBoxController::appendRow(
not_null<UserData*> user,
TimeId date) {
if (!delegate()->peerListFindRow(user->id.value)) {
_dates.emplace(user, date);
if (auto row = createRow(user, date)) {
delegate()->peerListAppendRow(std::move(row));
setDescriptionText(QString());
@@ -503,6 +553,7 @@ std::unique_ptr<PeerListRow> RequestsBoxController::createRow(
const auto search = static_cast<RequestsBoxSearchController*>(
searchController());
date = search->dateForUser(user);
_dates.emplace(user, date);
}
return std::make_unique<Row>(_helper.get(), user, date);
}
@@ -574,6 +625,36 @@ TimeId RequestsBoxSearchController::dateForUser(not_null<UserData*> user) {
return {};
}
auto RequestsBoxSearchController::saveState() const
-> std::unique_ptr<SavedStateBase> {
auto result = std::make_unique<SavedState>();
result->query = _query;
result->offsetDate = _offsetDate;
result->offsetUser = _offsetUser;
result->allLoaded = _allLoaded;
result->wasLoading = (_requestId != 0);
return result;
}
void RequestsBoxSearchController::restoreState(
std::unique_ptr<SavedStateBase> state) {
if (auto my = dynamic_cast<SavedState*>(state.get())) {
if (auto requestId = base::take(_requestId)) {
_api.request(requestId).cancel();
}
_cache.clear();
_queries.clear();
_allLoaded = my->allLoaded;
_offsetDate = my->offsetDate;
_offsetUser = my->offsetUser;
_query = my->query;
if (my->wasLoading) {
searchOnServer();
}
}
}
bool RequestsBoxSearchController::searchInCache() {
const auto i = _cache.find(_query);
if (i != _cache.cend()) {

View File

@@ -35,15 +35,32 @@ public:
Main::Session &session() const override;
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
void rowElementClicked(not_null<PeerListRow*> row, int element) override;
void rowElementClicked(
not_null<PeerListRow*> row,
int element) override;
void loadMoreRows() override;
std::unique_ptr<PeerListRow> createSearchRow(
not_null<PeerData*> peer) override;
std::unique_ptr<PeerListRow> createRestoredRow(
not_null<PeerData*> peer) override;
std::unique_ptr<PeerListState> saveState() const override;
void restoreState(std::unique_ptr<PeerListState> state) override;
private:
class RowHelper;
struct SavedState : SavedStateBase {
using SearchStateBase = PeerListSearchController::SavedStateBase;
std::unique_ptr<SearchStateBase> searchState;
base::flat_map<not_null<UserData*>, TimeId> dates;
TimeId offsetDate = 0;
UserData *offsetUser = nullptr;
bool allLoaded = false;
bool wasLoading = false;
};
static std::unique_ptr<PeerListSearchController> CreateSearchController(
not_null<PeerData*> peer);
@@ -63,6 +80,8 @@ private:
not_null<PeerData*> _peer;
MTP::Sender _api;
base::flat_map<not_null<UserData*>, TimeId> _dates;
TimeId _offsetDate = 0;
UserData *_offsetUser = nullptr;
mtpRequestId _loadRequestId = 0;
@@ -82,7 +101,17 @@ public:
void removeFromCache(not_null<UserData*> user);
[[nodiscard]] TimeId dateForUser(not_null<UserData*> user);
std::unique_ptr<SavedStateBase> saveState() const override;
void restoreState(std::unique_ptr<SavedStateBase> state) override;
private:
struct SavedState : SavedStateBase {
QString query;
TimeId offsetDate = 0;
UserData *offsetUser = nullptr;
bool allLoaded = false;
bool wasLoading = false;
};
struct Item {
not_null<UserData*> user;
TimeId date = 0;

View File

@@ -210,7 +210,7 @@ void ProcessFullPhoto(
) | rpl::map([=] {
const auto user = peer->asUser();
const auto username = peer->username();
const auto channelId = user->personalChannelId();
const auto channelId = user ? user->personalChannelId() : 0;
const auto channel = channelId
? user->owner().channel(channelId).get()
: nullptr;

View File

@@ -77,7 +77,7 @@ bool operator==(const Descriptor &a, const Descriptor &b) {
struct Preload {
Descriptor descriptor;
std::shared_ptr<Data::DocumentMedia> media;
std::weak_ptr<ChatHelpers::Show> show;
std::weak_ptr<Main::SessionShow> show;
};
[[nodiscard]] std::vector<Preload> &Preloads() {

View File

@@ -1388,7 +1388,8 @@ void ShareBox::Inner::applyChatFilter(FilterId id) {
const auto addList = [&](not_null<Dialogs::IndexedList*> list) {
for (const auto &row : list->all()) {
if (const auto history = row->history()) {
if (_descriptor.filterCallback(history)) {
if (history->asForum()
|| _descriptor.filterCallback(history)) {
_customChatsIndexed->addToEnd(history);
}
}

View File

@@ -139,6 +139,23 @@ private:
};
[[nodiscard]] bool SortForBirthday(not_null<PeerData*> peer) {
const auto user = peer->asUser();
if (!user) {
return false;
}
const auto birthday = user->birthday();
if (!birthday) {
return false;
}
const auto is = [&](const QDate &date) {
return (date.day() == birthday.day())
&& (date.month() == birthday.month());
};
const auto now = QDate::currentDate();
return is(now) || is(now.addDays(1)) || is(now.addDays(-1));
}
PreviewDelegate::PreviewDelegate(
not_null<QWidget*> parent,
not_null<Ui::ChatStyle*> st,
@@ -216,7 +233,7 @@ auto GenerateGiftMedia(
return tr::lng_action_gift_got_stars_text(
tr::now,
lt_count,
gift.info.convertStars,
gift.info.starsConverted,
Ui::Text::RichLangValue);
});
auto description = data.text.empty()
@@ -1068,10 +1085,23 @@ void SendGiftBox(
const auto padding = st::giftBoxPadding;
const auto available = width - padding.left() - padding.right();
const auto perRow = available / single.width();
const auto count = int(gifts.list.size());
auto order = ranges::views::ints
| ranges::views::take(count)
| ranges::to_vector;
if (SortForBirthday(peer)) {
ranges::stable_partition(order, [&](int i) {
const auto &gift = gifts.list[i];
const auto stars = std::get_if<GiftTypeStars>(&gift);
return stars && stars->info.birthday;
});
}
auto x = padding.left();
auto y = padding.top();
state->buttons.resize(gifts.list.size());
state->buttons.resize(count);
for (auto &button : state->buttons) {
if (!button) {
button = std::make_unique<GiftButton>(raw, &state->delegate);
@@ -1079,9 +1109,9 @@ void SendGiftBox(
}
}
const auto api = gifts.api;
for (auto i = 0, count = int(gifts.list.size()); i != count; ++i) {
for (auto i = 0; i != count; ++i) {
const auto button = state->buttons[i].get();
const auto &descriptor = gifts.list[i];
const auto &descriptor = gifts.list[order[i]];
button->setDescriptor(descriptor);
const auto last = !((i + 1) % perRow);
@@ -1108,12 +1138,12 @@ void SendGiftBox(
}
});
}
if (gifts.list.size() % perRow) {
if (count % perRow) {
y += padding.bottom() + single.height();
} else {
y += padding.bottom() - st::giftBoxGiftSkip.y();
}
raw->resize(raw->width(), gifts.list.empty() ? 0 : y);
raw->resize(raw->width(), count ? y : 0);
}, raw->lifetime());
return result;

View File

@@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "calls/group/calls_group_common.h"
#include "data/data_peer.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
#include "lang/lang_keys.h"
#include "main/main_account.h"
#include "main/main_session.h"
@@ -169,7 +171,12 @@ void StartRtmpProcess::finish(JoinInfo info) {
void StartRtmpProcess::createBox() {
auto done = [=] {
const auto peer = _request->peer;
finish({ .peer = peer, .joinAs = peer, .rtmp = true });
const auto joinAs = (peer->isChat() && peer->asChat()->amCreator())
? peer
: (peer->isChannel() && peer->asChannel()->amCreator())
? peer
: peer->session().user();
finish({ .peer = peer, .joinAs = joinAs, .rtmp = true });
};
auto revoke = [=] {
const auto guard = base::make_weak(&_request->guard);

View File

@@ -822,7 +822,7 @@ void StickersListFooter::mousePressEvent(QMouseEvent *e) {
if (e->button() != Qt::LeftButton) {
return;
}
_iconsMousePos = e ? e->globalPos() : QCursor::pos();
_iconsMousePos = e->globalPos();
updateSelected();
if (_selected == SpecialOver::Settings) {

View File

@@ -192,9 +192,7 @@ std::unique_ptr<Lottie::SinglePlayer> LottieThumbnail(
};
const auto session = thumb
? &thumb->owner()->session()
: media
? &media->owner()->session()
: nullptr;
: &media->owner()->session();
return LottieCachedFromContent(
method,
baseKey,

View File

@@ -189,6 +189,7 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const {
const auto game = media ? media->game() : nullptr;
if (url.startsWith(u"tg://"_q, Qt::CaseInsensitive) || !_bot || !game) {
openLink();
return;
}
const auto bot = _bot;
const auto title = game->title;

View File

@@ -240,7 +240,7 @@ QByteArray Settings::serialize() const {
+ Serialize::stringSize(_customFontFamily)
+ sizeof(qint32) * 3
+ Serialize::bytearraySize(_tonsiteStorageToken)
+ sizeof(qint32) * 6;
+ sizeof(qint32) * 7;
auto result = QByteArray();
result.reserve(size);
@@ -309,7 +309,7 @@ QByteArray Settings::serialize() const {
<< qint32(_thirdSectionExtendedBy)
<< qint32(_notifyFromAll ? 1 : 0)
<< qint32(_nativeWindowFrame.current() ? 1 : 0)
<< qint32(_systemDarkModeEnabled.current() ? 1 : 0)
<< qint32(0) // Legacy system dark mode
<< _cameraDeviceId.current()
<< qint32(_ipRevealWarning ? 1 : 0)
<< qint32(_groupCallPushToTalk ? 1 : 0)
@@ -400,7 +400,8 @@ QByteArray Settings::serialize() const {
<< qint32(_skipToastsInFocus ? 1 : 0)
<< qint32(_recordVideoMessages ? 1 : 0)
<< SerializeVideoQuality(_videoQuality)
<< qint32(_ivZoom.current());
<< qint32(_ivZoom.current())
<< qint32(_systemDarkModeEnabled.current() ? 1 : 0);
}
Ensures(result.size() == size);
@@ -610,6 +611,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
stream >> nativeWindowFrame;
}
if (!stream.atEnd()) {
// Read over this one below, if was in the file.
stream >> systemDarkModeEnabled;
}
if (!stream.atEnd()) {
@@ -853,6 +855,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) {
stream >> ivZoom;
}
if (!stream.atEnd()) {
stream >> systemDarkModeEnabled;
}
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()"));
@@ -1453,7 +1458,6 @@ void Settings::resetOnLastLogout() {
_thirdColumnWidth = kDefaultThirdColumnWidth; // p-w
_notifyFromAll = true;
_tabbedReplacedWithInfo = false; // per-window
_systemDarkModeEnabled = false;
_hiddenGroupCallTooltips = 0;
_storiesClickTooltipHidden = false;
_ttlVoiceClickTooltipHidden = false;

View File

@@ -1036,7 +1036,7 @@ private:
bool _notifyFromAll = true;
rpl::variable<bool> _nativeWindowFrame = false;
rpl::variable<std::optional<bool>> _systemDarkMode = std::nullopt;
rpl::variable<bool> _systemDarkModeEnabled = false;
rpl::variable<bool> _systemDarkModeEnabled = true;
rpl::variable<WindowTitleContent> _windowTitleContent;
WindowPosition _windowPosition; // per-window
bool _disableOpenGL = false;

View File

@@ -618,6 +618,7 @@ bool ResolveUsernameOrPhone(
.startAutoSubmit = myContext.botStartAutoSubmit,
.botAppName = (appname.isEmpty() ? postParam : appname),
.botAppForceConfirmation = myContext.mayShowConfirmation,
.botAppFullScreen = (params.value(u"mode"_q) == u"fullscreen"_q),
.attachBotUsername = params.value(u"attach"_q),
.attachBotToggleCommand = (params.contains(u"startattach"_q)
? params.value(u"startattach"_q)

View File

@@ -340,12 +340,12 @@ void PhoneClickHandler::onClick(ClickContext context) const {
if (Trim(phone) != Trim(controller->session().user()->phone())) {
menu->addAction(
tr::lng_info_add_as_contact(tr::now),
[=, raw = resolvePhoneAction.get()] {
[=, raw = Ui::MakeWeak(resolvePhoneAction.get())] {
controller->show(
Box<AddContactBox>(
_session,
raw->firstName(),
raw->lastName(),
&controller->session(),
raw ? raw->firstName() : QString(),
raw ? raw->lastName() : QString(),
Trim(phone)));
},
&st::menuIconInvite);

View File

@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
constexpr auto AppName = "Telegram Desktop"_cs;
constexpr auto AppFile = "Telegram"_cs;
constexpr auto AppVersion = 5007003;
constexpr auto AppVersionStr = "5.7.3";
constexpr auto AppBetaVersion = true;
constexpr auto AppVersion = 5008002;
constexpr auto AppVersionStr = "5.8.2";
constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -126,7 +126,6 @@ void RecentPeers::applyLocal(QByteArray serialized) {
).arg(count));
DEBUG_LOG(("Failed bytes: %1.").arg(
QString::fromUtf8(serialized.mid(streamPosition).toHex())));
_list.clear();
return;
}
}

View File

@@ -69,19 +69,20 @@ struct CreditsHistoryEntry final {
QString successLink;
int limitedCount = 0;
int limitedLeft = 0;
int convertStars = 0;
int starsConverted = 0;
int floodSkip = 0;
bool converted = false;
bool anonymous = false;
bool savedToProfile = false;
bool fromGiftsList = false;
bool soldOutInfo = false;
bool reaction = false;
bool refunded = false;
bool pending = false;
bool failed = false;
bool in = false;
bool gift = false;
bool converted : 1 = false;
bool anonymous : 1 = false;
bool stargift : 1 = false;
bool savedToProfile : 1 = false;
bool fromGiftsList : 1 = false;
bool soldOutInfo : 1 = false;
bool reaction : 1 = false;
bool refunded : 1 = false;
bool pending : 1 = false;
bool failed : 1 = false;
bool in : 1 = false;
bool gift : 1 = false;
};
struct CreditsStatusSlice final {

View File

@@ -745,6 +745,14 @@ bool DocumentData::emojiUsesTextColor() const {
return (_flags & Flag::UseTextColor);
}
void DocumentData::overrideEmojiUsesTextColor(bool value) {
if (value) {
_flags |= Flag::UseTextColor;
} else {
_flags &= ~Flag::UseTextColor;
}
}
bool DocumentData::hasThumbnail() const {
return _thumbnail.location.valid()
&& !thumbnailFailed()

View File

@@ -206,6 +206,7 @@ public:
[[nodiscard]] bool isPremiumSticker() const;
[[nodiscard]] bool isPremiumEmoji() const;
[[nodiscard]] bool emojiUsesTextColor() const;
void overrideEmojiUsesTextColor(bool value);
[[nodiscard]] bool hasThumbnail() const;
[[nodiscard]] bool thumbnailLoading() const;

View File

@@ -139,7 +139,7 @@ struct GiftCode {
TextWithEntities message;
ChannelData *channel = nullptr;
MsgId giveawayMsgId = 0;
int convertStars = 0;
int starsConverted = 0;
int limitedCount = 0;
int limitedLeft = 0;
int count = 0;

View File

@@ -1225,6 +1225,9 @@ not_null<CustomEmojiManager::Listener*> Reactions::resolveListener() {
}
void Reactions::customEmojiResolveDone(not_null<DocumentData*> document) {
if (!document->sticker()) {
return;
}
const auto id = ReactionId{ { document->id } };
const auto favorite = (_unresolvedFavoriteId == id);
const auto i = _unresolvedTop.find(id);

View File

@@ -17,11 +17,16 @@ ChatBotCommands::Changed ChatBotCommands::update(
clear();
} else {
for (const auto &commands : list) {
auto &value = operator[](commands.userId);
changed |= commands.commands.empty()
? remove(commands.userId)
: !ranges::equal(value, commands.commands);
value = commands.commands;
if (commands.commands.empty()) {
changed |= remove(commands.userId);
} else {
auto &value = operator[](commands.userId);
const auto isEqual = ranges::equal(value, commands.commands);
changed |= !isEqual;
if (!isEqual) {
value = commands.commands;
}
}
}
}
return changed;

View File

@@ -345,6 +345,24 @@ void UserData::setBotInfo(const MTPBotInfo &info) {
const auto privacyChanged = (botInfo->privacyPolicyUrl != privacy);
botInfo->privacyPolicyUrl = privacy;
if (const auto settings = d.vapp_settings()) {
const auto &data = settings->data();
botInfo->botAppColorTitleDay = Ui::MaybeColorFromSerialized(
data.vheader_color()).value_or(QColor(0, 0, 0, 0));
botInfo->botAppColorTitleNight = Ui::MaybeColorFromSerialized(
data.vheader_dark_color()).value_or(QColor(0, 0, 0, 0));
botInfo->botAppColorBodyDay = Ui::MaybeColorFromSerialized(
data.vbackground_color()).value_or(QColor(0, 0, 0, 0));
botInfo->botAppColorBodyNight = Ui::MaybeColorFromSerialized(
data.vbackground_dark_color()).value_or(QColor(0, 0, 0, 0));
} else {
botInfo->botAppColorTitleDay
= botInfo->botAppColorTitleNight
= botInfo->botAppColorBodyDay
= botInfo->botAppColorBodyNight
= QColor(0, 0, 0, 0);
}
if (changedCommands || changedButton || privacyChanged) {
owner().botCommandsChanged(this);
}
@@ -580,6 +598,9 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
} else {
user->setBotInfoVersion(-1);
}
if (const auto info = user->botInfo.get()) {
info->canManageEmojiStatus = update.is_bot_can_manage_emoji_status();
}
if (const auto pinned = update.vpinned_msg_id()) {
SetTopPinnedMessageId(user, pinned->v);
}

View File

@@ -33,6 +33,11 @@ struct BotInfo {
QString botMenuButtonUrl;
QString privacyPolicyUrl;
QColor botAppColorTitleDay = QColor(0, 0, 0, 0);
QColor botAppColorTitleNight = QColor(0, 0, 0, 0);
QColor botAppColorBodyDay = QColor(0, 0, 0, 0);
QColor botAppColorBodyNight = QColor(0, 0, 0, 0);
QString startToken;
Dialogs::EntryState inlineReturnTo;
@@ -47,6 +52,7 @@ struct BotInfo {
bool cantJoinGroups : 1 = false;
bool supportsAttachMenu : 1 = false;
bool canEditInformation : 1 = false;
bool canManageEmojiStatus : 1 = false;
bool supportsBusiness : 1 = false;
bool hasMainApp : 1 = false;
};

View File

@@ -652,22 +652,27 @@ void CustomEmojiManager::unregisterListener(not_null<Listener*> listener) {
}
}
rpl::producer<not_null<DocumentData*>> CustomEmojiManager::resolve(
DocumentId documentId) {
auto CustomEmojiManager::resolve(DocumentId documentId)
-> rpl::producer<not_null<DocumentData*>, rpl::empty_error> {
return [=](auto consumer) {
auto result = rpl::lifetime();
const auto put = [=](not_null<DocumentData*> document) {
const auto put = [=](
not_null<DocumentData*> document,
bool resolved = true) {
if (!document->sticker()) {
if (resolved) {
consumer.put_error({});
}
return false;
}
consumer.put_next_copy(document);
return true;
};
if (!put(owner().document(documentId))) {
const auto listener = new CallbackListener(put);
if (!put(owner().document(documentId), false)) {
const auto listener = result.make_state<CallbackListener>(put);
resolve(documentId, listener);
result.add([=] {
unregisterListener(listener);
delete listener;
});
}
return result;
@@ -763,6 +768,9 @@ void CustomEmojiManager::request() {
requestFinished();
}).fail([=] {
LOG(("API Error: Failed to get documents for emoji."));
for (const auto &id : ids) {
processListeners(_owner->document(id.v));
}
requestFinished();
}).send();
}
@@ -792,7 +800,8 @@ void CustomEmojiManager::processLoaders(not_null<DocumentData*> document) {
}
}
void CustomEmojiManager::processListeners(not_null<DocumentData*> document) {
void CustomEmojiManager::processListeners(
not_null<DocumentData*> document) {
const auto id = document->id;
if (const auto listeners = _resolvers.take(id)) {
for (const auto &listener : *listeners) {

View File

@@ -66,8 +66,8 @@ public:
void resolve(DocumentId documentId, not_null<Listener*> listener);
void unregisterListener(not_null<Listener*> listener);
[[nodiscard]] rpl::producer<not_null<DocumentData*>> resolve(
DocumentId documentId);
[[nodiscard]] auto resolve(DocumentId documentId)
-> rpl::producer<not_null<DocumentData*>, rpl::empty_error>;
[[nodiscard]] std::unique_ptr<Ui::CustomEmoji::Loader> createLoader(
not_null<DocumentData*> document,

View File

@@ -206,22 +206,20 @@ void Stickers::incrementSticker(not_null<DocumentData*> document) {
auto &sets = setsRef();
auto it = sets.find(Data::Stickers::CloudRecentSetId);
if (it == sets.cend()) {
if (it == sets.cend()) {
it = sets.emplace(
it = sets.emplace(
Data::Stickers::CloudRecentSetId,
std::make_unique<Data::StickersSet>(
&session().data(),
Data::Stickers::CloudRecentSetId,
std::make_unique<Data::StickersSet>(
&session().data(),
Data::Stickers::CloudRecentSetId,
uint64(0), // accessHash
uint64(0), // hash
tr::lng_recent_stickers(tr::now),
QString(),
0, // count
SetFlag::Special,
TimeId(0))).first;
} else {
it->second->title = tr::lng_recent_stickers(tr::now);
}
uint64(0), // accessHash
uint64(0), // hash
tr::lng_recent_stickers(tr::now),
QString(),
0, // count
SetFlag::Special,
TimeId(0))).first;
} else {
it->second->title = tr::lng_recent_stickers(tr::now);
}
const auto set = it->second.get();
auto removedFromEmoji = std::vector<not_null<EmojiPtr>>();

View File

@@ -692,6 +692,10 @@ dialogsSearchTabs: SettingsSlider(defaultSettingsSlider) {
}
dialogsSearchTabsPadding: 8px;
chatsFiltersTabs: SettingsSlider(dialogsSearchTabs) {
rippleBottomSkip: 0px;
}
dialogsStoriesList: DialogsStoriesList {
small: dialogsStories;
full: dialogsStoriesFull;

View File

@@ -653,7 +653,8 @@ Widget::Widget(
}
if (session().settings().dialogsFiltersEnabled()
&& Core::App().settings().chatFiltersHorizontal()) {
&& (Core::App().settings().chatFiltersHorizontal()
|| !controller->enoughSpaceForFilters())) {
toggleFiltersMenu(true);
}
}
@@ -1301,19 +1302,38 @@ void Widget::updateHasFocus(not_null<QWidget*> focused) {
}
void Widget::toggleFiltersMenu(bool enabled) {
if (_layout == Layout::Child) {
enabled = false;
}
if (!enabled == !_chatFilters) {
return;
} else if (enabled) {
_chatFilters = base::make_unique_q<Ui::RpWidget>(this);
class NoScrollPropagationWidget final : public Ui::RpWidget {
public:
using Ui::RpWidget::RpWidget;
protected:
void touchEvent(QTouchEvent *e) {
e->accept();
}
void wheelEvent(QWheelEvent *e) override final {
e->accept();
}
};
_chatFilters = base::make_unique_q<NoScrollPropagationWidget>(this);
const auto raw = _chatFilters.get();
const auto inner = Ui::AddChatFiltersTabsStrip(
_chatFilters.get(),
&session(),
[this](FilterId id) {
_scroll->scrollToY(0);
if (controller()->activeChatsFilterCurrent() != id) {
controller()->setActiveChatsFilter(id);
}
},
controller(),
true);
raw->show();
raw->stackUnder(_scroll);

View File

@@ -296,7 +296,7 @@ private:
bool _dragForward = false;
base::Timer _chooseByDragTimer;
Layout _layout = Layout::Main;
const Layout _layout = Layout::Main;
int _narrowWidth = 0;
object_ptr<Ui::RpWidget> _searchControls;
object_ptr<HistoryView::TopBarWidget> _subsectionTopBar = { nullptr };

View File

@@ -438,8 +438,6 @@ void ApiWrap::startExport(
}
if (_settings->types & Settings::Type::AnyChatsMask) {
_startProcess->steps.push_back(Step::SplitRanges);
}
if (_settings->types & Settings::Type::AnyChatsMask) {
_startProcess->steps.push_back(Step::DialogsCount);
}
if (_settings->types & Settings::Type::GroupsChannelsMask) {

View File

@@ -712,6 +712,14 @@ not_null<HistoryItem*> History::addNewLocalMessage(
true);
}
not_null<HistoryItem*> History::addNewLocalMessage(
not_null<HistoryItem*> item) {
Expects(item->history() == this);
Expects(item->isLocal());
return addNewItem(item, true);
}
not_null<HistoryItem*> History::addSponsoredMessage(
MsgId id,
Data::SponsoredFrom from,

View File

@@ -180,6 +180,7 @@ public:
not_null<HistoryItem*> addNewLocalMessage(
HistoryItemCommonFields &&fields,
not_null<GameData*> game);
not_null<HistoryItem*> addNewLocalMessage(not_null<HistoryItem*> item);
not_null<HistoryItem*> addSponsoredMessage(
MsgId id,

View File

@@ -2435,6 +2435,14 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
if (const auto sticker = emojiStickers->stickerForEmoji(
isolated)) {
addDocumentActions(sticker.document, item);
} else if (v::is<QString>(isolated.items.front())
&& v::is_null(isolated.items[1])) {
const auto id = v::get<QString>(isolated.items.front());
const auto docId = id.toULongLong();
const auto document = session->data().document(docId);
if (document->sticker()) {
addDocumentActions(document, item);
}
}
}
}

View File

@@ -5530,7 +5530,7 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
data.vmessage()->data().ventities().v),
}
: TextWithEntities()),
.convertStars = int(data.vconvert_stars().v),
.starsConverted = int(data.vconvert_stars().value_or_empty()),
.limitedCount = gift.vavailability_total().value_or_empty(),
.limitedLeft = gift.vavailability_remains().value_or_empty(),
.count = int(gift.vstars().v),

View File

@@ -14,28 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
[[nodiscard]] InlineBots::PeerTypes PeerTypesFromMTP(
const MTPvector<MTPInlineQueryPeerType> &types) {
using namespace InlineBots;
auto result = PeerTypes(0);
for (const auto &type : types.v) {
result |= type.match([&](const MTPDinlineQueryPeerTypePM &data) {
return PeerType::User;
}, [&](const MTPDinlineQueryPeerTypeChat &data) {
return PeerType::Group;
}, [&](const MTPDinlineQueryPeerTypeMegagroup &data) {
return PeerType::Group;
}, [&](const MTPDinlineQueryPeerTypeBroadcast &data) {
return PeerType::Broadcast;
}, [&](const MTPDinlineQueryPeerTypeBotPM &data) {
return PeerType::Bot;
}, [&](const MTPDinlineQueryPeerTypeSameBotPM &data) {
return PeerType();
});
}
return result;
}
[[nodiscard]] RequestPeerQuery RequestPeerQueryFromTL(
const MTPDkeyboardButtonRequestPeer &query) {
using Type = RequestPeerQuery::Type;
@@ -76,6 +54,28 @@ namespace {
} // namespace
InlineBots::PeerTypes PeerTypesFromMTP(
const MTPvector<MTPInlineQueryPeerType> &types) {
using namespace InlineBots;
auto result = PeerTypes(0);
for (const auto &type : types.v) {
result |= type.match([&](const MTPDinlineQueryPeerTypePM &data) {
return PeerType::User;
}, [&](const MTPDinlineQueryPeerTypeChat &data) {
return PeerType::Group;
}, [&](const MTPDinlineQueryPeerTypeMegagroup &data) {
return PeerType::Group;
}, [&](const MTPDinlineQueryPeerTypeBroadcast &data) {
return PeerType::Broadcast;
}, [&](const MTPDinlineQueryPeerTypeBotPM &data) {
return PeerType::Bot;
}, [&](const MTPDinlineQueryPeerTypeSameBotPM &data) {
return PeerType();
});
}
return result;
}
HistoryMessageMarkupButton::HistoryMessageMarkupButton(
Type type,
const QString &text,

View File

@@ -19,6 +19,9 @@ enum class PeerType : uint8;
using PeerTypes = base::flags<PeerType>;
} // namespace InlineBots
[[nodiscard]] InlineBots::PeerTypes PeerTypesFromMTP(
const MTPvector<MTPInlineQueryPeerType> &types);
enum class ReplyMarkupFlag : uint32 {
None = (1U << 0),
ForceReply = (1U << 1),

View File

@@ -869,7 +869,7 @@ HistoryWidget::HistoryWidget(
}
if (flags & PeerUpdateFlag::FullInfo) {
fullInfoUpdated();
if (const auto channel = _peer ? _peer->asChannel() : nullptr) {
if (const auto channel = _peer->asChannel()) {
if (channel->allowedReactions().paidEnabled) {
session().credits().load();
}
@@ -3440,7 +3440,7 @@ void HistoryWidget::messagesFailed(const MTP::Error &error, int requestId) {
closeCurrent();
const auto wasAccount = not_null(&was->account());
if (const auto primary = Core::App().windowFor(wasAccount)) {
primary->showToast((was && was->isMegagroup())
primary->showToast(was->isMegagroup()
? tr::lng_group_not_accessible(tr::now)
: tr::lng_channel_not_accessible(tr::now));
}
@@ -5111,7 +5111,7 @@ bool HistoryWidget::updateCmdStartShown() {
const auto textSmall = _fieldCharsCountManager.count() > kSmallMenuAfter;
const auto textChanged = _botMenu.button
&& ((_botMenu.text != bot->botInfo->botMenuButtonText)
|| (_botMenu.small != textSmall));
|| (_botMenu.small != textSmall));
if (textChanged) {
_botMenu.text = bot->botInfo->botMenuButtonText;
if ((_botMenu.small = textSmall)) {
@@ -7701,12 +7701,12 @@ void HistoryWidget::createSponsoredMessageBar() {
session().sponsoredMessages().itemRemoved(
maybeFullId
) | rpl::start_with_next([this] {
_sponsoredMessageBar->toggle(false, anim::type::normal);
_sponsoredMessageBar->shownValue() | rpl::filter(
!rpl::mappers::_1
) | rpl::start_with_next([this] {
_sponsoredMessageBar = nullptr;
}, _sponsoredMessageBar->lifetime());
_sponsoredMessageBar->toggle(false, anim::type::normal);
}, _sponsoredMessageBar->lifetime());
if (maybeFullId) {

View File

@@ -381,7 +381,7 @@ void FieldHeader::init() {
return;
}
const auto e = static_cast<QMouseEvent*>(event.get());
const auto pos = e ? e->pos() : mapFromGlobal(QCursor::pos());
const auto pos = e->pos();
const auto inPreviewRect = _clickableRect.contains(pos);
const auto inPhotoEdit = _shownMessageHasPreview
&& _photoEditAllowed
@@ -1191,9 +1191,7 @@ void ComposeControls::showStarted() {
if (_attachBotsMenu) {
_attachBotsMenu->hideFast();
}
if (_voiceRecordBar) {
_voiceRecordBar->hideFast();
}
_voiceRecordBar->hideFast();
if (_autocomplete) {
_autocomplete->hideFast();
}
@@ -1213,9 +1211,7 @@ void ComposeControls::showFinished() {
if (_attachBotsMenu) {
_attachBotsMenu->hideFast();
}
if (_voiceRecordBar) {
_voiceRecordBar->hideFast();
}
_voiceRecordBar->hideFast();
if (_autocomplete) {
_autocomplete->hideFast();
}
@@ -3383,6 +3379,10 @@ Fn<void()> ComposeControls::restoreTextCallback(
});
}
Ui::InputField *ComposeControls::fieldForMention() const {
return _writeRestriction.current() ? nullptr : _field.get();
}
TextWithEntities ComposeControls::prepareTextForEditMsg() const {
if (!_history) {
return {};

View File

@@ -249,6 +249,8 @@ public:
Fn<void()> restoreTextCallback(const QString &insertTextOnCancel) const;
[[nodiscard]] Ui::InputField *fieldForMention() const;
private:
enum class TextUpdateEvent {
SaveDraft = (1 << 0),

View File

@@ -474,7 +474,7 @@ TTLButton::TTLButton(
) | rpl::start_with_next([=](bool toHide) {
const auto isFirstTooltip
= !Core::App().settings().ttlVoiceClickTooltipHidden();
if (isFirstTooltip || (!isFirstTooltip && toHide)) {
if (isFirstTooltip || toHide) {
_tooltip->toggleAnimated(!toHide);
}
}, _tooltip->lifetime());

View File

@@ -161,6 +161,8 @@ private:
bool listShowReactPremiumError(
not_null<HistoryItem*> item,
const Data::ReactionId &id) override;
base::unique_qptr<Ui::PopupMenu> listFillSenderUserpicMenu(
PeerId userpicPeerId) override;
void listWindowSetInnerFocus() override;
bool listAllowsDragForward() override;
void listLaunchDrag(
@@ -828,6 +830,11 @@ bool Item::listShowReactPremiumError(
return false;
}
base::unique_qptr<Ui::PopupMenu> Item::listFillSenderUserpicMenu(
PeerId userpicPeerId) {
return nullptr;
}
void Item::listWindowSetInnerFocus() {
}

View File

@@ -98,7 +98,7 @@ namespace {
text.size(),
Data::SerializeCustomEmojiId(document)) },
};
});
}) | rpl::map_error_to_done();
}
[[nodiscard]] rpl::producer<TextWithEntities> PeerCustomStatus(

View File

@@ -1510,11 +1510,13 @@ void AddWhoReactedAction(
strong->hideMenu();
}
if (const auto item = controller->session().data().message(itemId)) {
controller->window().show(Reactions::FullListBox(
controller,
item,
{},
whoReadIds));
controller->showSection(
std::make_shared<Info::Memento>(
whoReadIds,
itemId,
HistoryView::Reactions::DefaultSelectedTab(
item,
whoReadIds)));
}
};
if (!menu->empty()) {
@@ -1685,10 +1687,10 @@ void ShowWhoReactedMenu(
};
const auto showAllChosen = [=, itemId = item->fullId()]{
if (const auto item = controller->session().data().message(itemId)) {
controller->window().show(Reactions::FullListBox(
controller,
item,
id));
controller->showSection(std::make_shared<Info::Memento>(
nullptr,
itemId,
HistoryView::Reactions::DefaultSelectedTab(item, id)));
}
};
const auto owner = &controller->session().data();

View File

@@ -30,9 +30,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/message_field.h"
#include "mainwindow.h"
#include "mainwidget.h"
#include "core/click_handler_types.h"
#include "core/application.h"
#include "core/click_handler_types.h"
#include "core/core_settings.h"
#include "core/phone_click_handler.h"
#include "apiwrap.h"
#include "api/api_who_reacted.h"
#include "api/api_views.h"
@@ -171,6 +172,11 @@ bool WindowListDelegate::listShowReactPremiumError(
return Window::ShowReactPremiumError(_window, item, id);
}
auto WindowListDelegate::listFillSenderUserpicMenu(PeerId userpicPeerId)
-> base::unique_qptr<Ui::PopupMenu> {
return nullptr;
}
void WindowListDelegate::listWindowSetInnerFocus() {
_window->widget()->setInnerFocus();
}
@@ -2794,6 +2800,12 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
? _overElement->data().get()
: nullptr;
const auto clickedReaction = Reactions::ReactionIdOfLink(link);
const auto linkPhoneNumber = link
? link->property(kPhoneNumberLinkProperty).toString()
: QString();
const auto linkUserpicPeerId = (link && _overSenderUserpic)
? PeerId(link->property(kPeerLinkPeerIdProperty).toULongLong())
: PeerId();
_whoReactedMenuLifetime.destroy();
if (!clickedReaction.empty()
&& overItem
@@ -2808,6 +2820,19 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
_whoReactedMenuLifetime);
e->accept();
return;
} else if (!linkPhoneNumber.isEmpty()) {
PhoneClickHandler(&session(), linkPhoneNumber).onClick(
prepareClickContext(
Qt::LeftButton,
_overItemExact ? _overItemExact->fullId() : FullMsgId()));
return;
} else if (linkUserpicPeerId) {
_menu = _delegate->listFillSenderUserpicMenu(linkUserpicPeerId);
if (_menu) {
_menu->popup(e->globalPos());
e->accept();
return;
}
}
auto request = ContextMenuRequest(controller());
@@ -3568,6 +3593,15 @@ ClickHandlerContext ListWidget::prepareClickHandlerContext(FullMsgId id) {
};
}
ClickContext ListWidget::prepareClickContext(
Qt::MouseButton button,
FullMsgId itemId) {
return {
button,
QVariant::fromValue(prepareClickHandlerContext(itemId)),
};
}
int ListWidget::SelectionViewOffset(
not_null<const ListWidget*> inner,
not_null<const Element*> view) {
@@ -3629,6 +3663,7 @@ void ListWidget::mouseActionUpdate() {
auto inTextSelection = (_overState.pointState != PointState::Outside)
&& (_overState.itemId == _pressState.itemId)
&& hasSelectedText();
auto dragStateUserpic = false;
const auto overReaction = reactionView && reactionState.link;
if (overReaction) {
dragState = reactionState;
@@ -3727,6 +3762,7 @@ void ListWidget::mouseActionUpdate() {
// stop enumeration if we've found a userpic under the cursor
if (point.y() >= userpicTop && point.y() < userpicTop + st::msgPhotoSize) {
dragState = TextState(nullptr, view->fromPhotoLink());
dragStateUserpic = true;
_overItemExact = nullptr;
lnkhost = view;
return false;
@@ -3738,6 +3774,7 @@ void ListWidget::mouseActionUpdate() {
}
}
const auto lnkChanged = ClickHandler::setActive(dragState.link, lnkhost);
_overSenderUserpic = dragStateUserpic;
if (lnkChanged || dragState.cursor != _mouseCursorState) {
Ui::Tooltip::Hide();
}

View File

@@ -185,6 +185,8 @@ public:
virtual bool listShowReactPremiumError(
not_null<HistoryItem*> item,
const Data::ReactionId &id) = 0;
virtual base::unique_qptr<Ui::PopupMenu> listFillSenderUserpicMenu(
PeerId userpicPeerId) = 0;
virtual void listWindowSetInnerFocus() = 0;
virtual bool listAllowsDragForward() = 0;
virtual void listLaunchDrag(
@@ -218,6 +220,8 @@ public:
bool listShowReactPremiumError(
not_null<HistoryItem*> item,
const Data::ReactionId &id) override;
base::unique_qptr<Ui::PopupMenu> listFillSenderUserpicMenu(
PeerId userpicPeerId) override;
void listWindowSetInnerFocus() override;
bool listAllowsDragForward() override;
void listLaunchDrag(
@@ -356,6 +360,9 @@ public:
int top) const;
[[nodiscard]] ClickHandlerContext prepareClickHandlerContext(
FullMsgId id);
[[nodiscard]] ClickContext prepareClickContext(
Qt::MouseButton button,
FullMsgId itemId);
// AbstractTooltipShower interface
QString tooltipText() const override;
@@ -808,6 +815,7 @@ private:
CursorState _mouseCursorState = CursorState();
uint16 _mouseTextSymbol = 0;
bool _pressWasInactive = false;
bool _overSenderUserpic = false;
bool _selectEnabled = false;
HistoryItem *_selectedTextItem = nullptr;

View File

@@ -2120,6 +2120,7 @@ PointState Message::pointState(QPoint point) const {
// Entry page is always a bubble bottom.
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || check || (entry/* && entry->isBubbleBottom()*/);
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
if (item->repliesAreComments() || item->externalReply()) {
g.setHeight(g.height() - st::historyCommentsButtonHeight);
@@ -2160,11 +2161,18 @@ PointState Message::pointState(QPoint point) const {
trect.setHeight(trect.height() - entryHeight);
}
auto mediaHeight = media->height();
auto mediaLeft = trect.x() - st::msgPadding.left();
auto mediaTop = (trect.y() + trect.height() - mediaHeight);
if (point.y() >= mediaTop && point.y() < mediaTop + mediaHeight) {
const auto mediaHeight = mediaDisplayed ? media->height() : 0;
const auto mediaLeft = trect.x() - st::msgPadding.left();
const auto mediaTop = (!mediaDisplayed || _invertMedia)
? (trect.y() + (mediaOnTop ? 0 : st::mediaInBubbleSkip))
: (trect.y() + trect.height() - mediaHeight);
if (mediaDisplayed && _invertMedia) {
trect.setY(mediaTop
+ mediaHeight
+ (mediaOnBottom ? 0 : st::mediaInBubbleSkip));
}
if (point.y() >= mediaTop
&& point.y() < mediaTop + mediaHeight) {
return media->pointState(point - QPoint(mediaLeft, mediaTop));
}
}

View File

@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_view_swipe.h"
#include "ui/chat/pinned_bar.h"
#include "ui/chat/chat_style.h"
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/popup_menu.h"
@@ -915,8 +916,9 @@ void RepliesWidget::setupSwipeReply() {
result.callback = [=, itemId = view->data()->fullId()] {
const auto still = show->session().data().message(itemId);
const auto view = _inner->viewByPosition(still->position());
const auto selected = view->selectedQuote(
_inner->getSelectedTextRange(still));
const auto selected = view
? view->selectedQuote(_inner->getSelectedTextRange(still))
: SelectedQuote();
const auto replyToItemId = (selected.item
? selected.item
: still)->fullId();
@@ -2705,6 +2707,23 @@ Ui::ChatPaintContext RepliesWidget::listPreparePaintContext(
return context;
}
base::unique_qptr<Ui::PopupMenu> RepliesWidget::listFillSenderUserpicMenu(
PeerId userpicPeerId) {
const auto searchInEntry = _topic
? Dialogs::Key(_topic)
: Dialogs::Key(_history);
auto menu = base::make_unique_q<Ui::PopupMenu>(
this,
st::popupMenuWithIcons);
Window::FillSenderUserpicMenu(
controller(),
_history->owner().peer(userpicPeerId),
_composeControls->fieldForMention(),
searchInEntry,
Ui::Menu::CreateAddActionCallback(menu.get()));
return menu->empty() ? nullptr : std::move(menu);
}
void RepliesWidget::setupEmptyPainter() {
Expects(_topic != nullptr);

View File

@@ -183,6 +183,8 @@ public:
not_null<TranslateTracker*> tracker) override;
Ui::ChatPaintContext listPreparePaintContext(
Ui::ChatPaintContextArgs &&args) override;
base::unique_qptr<Ui::PopupMenu> listFillSenderUserpicMenu(
PeerId userpicPeerId) override;
// CornerButtonsDelegate delegate.
void cornerButtonsShowAtPosition(

View File

@@ -72,10 +72,8 @@ namespace {
constexpr auto kEmojiInteractionSeenDuration = 3 * crl::time(1000);
inline bool HasGroupCallMenu(const not_null<PeerData*> &peer) {
return !peer->groupCall()
&& ((peer->isChannel() && peer->asChannel()->amCreator())
|| (peer->isChat() && peer->asChat()->amCreator()));
[[nodiscard]] inline bool HasGroupCallMenu(not_null<PeerData*> peer) {
return !peer->groupCall() && peer->canManageGroupCall();
}
QString TopBarNameText(

View File

@@ -108,6 +108,9 @@ CustomEmoji::CustomEmoji(
}
void CustomEmoji::customEmojiResolveDone(not_null<DocumentData*> document) {
if (!document->sticker()) {
return;
}
_resolving = false;
const auto id = document->id;
for (auto &line : _lines) {

View File

@@ -80,7 +80,7 @@ TextWithEntities PremiumGift::subtitle() {
? tr::lng_action_gift_sent_text(
tr::now,
lt_count,
_data.convertStars,
_data.starsConverted,
lt_user,
Ui::Text::Bold(_parent->history()->peer->shortName()),
Ui::Text::RichLangValue)
@@ -89,7 +89,7 @@ TextWithEntities PremiumGift::subtitle() {
: tr::lng_action_gift_got_stars_text)(
tr::now,
lt_count,
_data.convertStars,
_data.starsConverted,
Ui::Text::RichLangValue);
}
const auto isCreditsPrize = creditsPrize();

View File

@@ -91,7 +91,8 @@ ThemeDocument::ThemeDocument(
: File(parent, parent->data())
, _data(document)
, _serviceWidth(serviceWidth) {
Expects(params.has_value() || _data->hasThumbnail() || _data->isTheme());
Expects(params.has_value()
|| (_data && (_data->hasThumbnail() || _data->isTheme())));
if (params) {
_background = params->backgroundColors();

View File

@@ -560,7 +560,9 @@ QSize WebPage::countOptimalSize() {
}
// init dimensions
const auto skipBlockWidth = _parent->skipBlockWidth();
const auto skipBlockWidth = (sponsored && sponsored->hasMedia)
? 0
: _parent->skipBlockWidth();
auto maxWidth = skipBlockWidth;
auto minHeight = 0;
@@ -628,8 +630,10 @@ QSize WebPage::countOptimalSize() {
_durationWidth = st::msgDateFont->width(_duration);
}
if (!_openButton.isEmpty()) {
maxWidth += rect::m::sum::h(st::historyPageButtonPadding)
+ _openButton.maxWidth();
accumulate_max(
maxWidth,
rect::m::sum::h(st::historyPageButtonPadding)
+ _openButton.maxWidth());
}
maxWidth += rect::m::sum::h(padding);
minHeight += rect::m::sum::v(padding);
@@ -1228,8 +1232,9 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
.position = QPoint(
inner.x() + (inner.width() - _openButton.maxWidth()) / 2,
end + st::historyPageButtonPadding.top()),
.availableWidth = paintw,
.availableWidth = inner.width(),
.now = context.now,
.elisionLines = 1,
});
}
}

View File

@@ -62,8 +62,8 @@ private:
class Controller final : public PeerListController {
public:
Controller(
not_null<Window::SessionController*> window,
not_null<HistoryItem*> item,
not_null<Window::SessionNavigation*> window,
FullMsgId itemId,
const ReactionId &selected,
rpl::producer<ReactionId> switches,
std::shared_ptr<Api::WhoReadList> whoReadIds);
@@ -73,9 +73,26 @@ public:
void rowClicked(not_null<PeerListRow*> row) override;
void loadMoreRows() override;
std::unique_ptr<PeerListRow> createRestoredRow(
not_null<PeerData*> peer) override;
std::unique_ptr<PeerListState> saveState() const override;
void restoreState(std::unique_ptr<PeerListState> state) override;
private:
using AllEntry = std::pair<not_null<PeerData*>, Data::ReactionId>;
struct SavedState : SavedStateBase {
ReactionId shownReaction;
base::flat_map<std::pair<PeerId, ReactionId>, uint64> idsMap;
uint64 idsCounter = 0;
std::vector<AllEntry> all;
QString allOffset;
std::vector<not_null<PeerData*>> filtered;
QString filteredOffset;
bool wasLoading = false;
};
void fillWhoRead();
void loadMore(const ReactionId &reaction);
bool appendRow(not_null<PeerData*> peer, ReactionId reaction);
@@ -88,14 +105,15 @@ private:
not_null<PeerData*> peer,
const ReactionId &reaction) const;
const not_null<Window::SessionController*> _window;
const not_null<HistoryItem*> _item;
const not_null<Window::SessionNavigation*> _window;
const not_null<PeerData*> _peer;
const FullMsgId _itemId;
const Ui::Text::CustomEmojiFactory _factory;
const std::shared_ptr<Api::WhoReadList> _whoReadIds;
const std::vector<not_null<PeerData*>> _whoRead;
MTP::Sender _api;
ReactionId _shownReaction;
std::shared_ptr<Api::WhoReadList> _whoReadIds;
std::vector<not_null<PeerData*>> _whoRead;
mutable base::flat_map<std::pair<PeerId, ReactionId>, uint64> _idsMap;
mutable uint64 _idsCounter = 0;
@@ -110,6 +128,22 @@ private:
};
[[nodiscard]] std::vector<not_null<PeerData*>> ResolveWhoRead(
not_null<Window::SessionNavigation*> window,
const std::shared_ptr<Api::WhoReadList> &whoReadIds) {
if (!whoReadIds || whoReadIds->list.empty()) {
return {};
}
auto result = std::vector<not_null<PeerData*>>();
auto &owner = window->session().data();
for (const auto &peerWithDate : whoReadIds->list) {
if (const auto peer = owner.peerLoaded(peerWithDate.peer)) {
result.push_back(peer);
}
}
return result;
}
Row::Row(
uint64 id,
not_null<PeerData*> peer,
@@ -166,17 +200,19 @@ void Row::rightActionPaint(
}
Controller::Controller(
not_null<Window::SessionController*> window,
not_null<HistoryItem*> item,
not_null<Window::SessionNavigation*> window,
FullMsgId itemId,
const ReactionId &selected,
rpl::producer<ReactionId> switches,
std::shared_ptr<Api::WhoReadList> whoReadIds)
: _window(window)
, _item(item)
, _peer(window->session().data().peer(itemId.peer))
, _itemId(itemId)
, _factory(Data::ReactedMenuFactory(&window->session()))
, _whoReadIds(whoReadIds)
, _whoRead(ResolveWhoRead(window, _whoReadIds))
, _api(&window->session().mtp())
, _shownReaction(selected)
, _whoReadIds(whoReadIds) {
, _shownReaction(selected) {
std::move(
switches
) | rpl::filter([=](const ReactionId &reaction) {
@@ -248,14 +284,6 @@ uint64 Controller::id(
}
void Controller::fillWhoRead() {
if (_whoReadIds && !_whoReadIds->list.empty() && _whoRead.empty()) {
auto &owner = _window->session().data();
for (const auto &peerWithDate : _whoReadIds->list) {
if (const auto peer = owner.peerLoaded(peerWithDate.peer)) {
_whoRead.push_back(peer);
}
}
}
for (const auto &peer : _whoRead) {
appendRow(peer, ReactionId());
}
@@ -271,6 +299,60 @@ void Controller::loadMoreRows() {
loadMore(_shownReaction);
}
std::unique_ptr<PeerListRow> Controller::createRestoredRow(
not_null<PeerData*> peer) {
if (_shownReaction.emoji() == u"read"_q) {
return createRow(peer, Data::ReactionId());
} else if (_shownReaction.empty()) {
const auto i = ranges::find(_all, peer, &AllEntry::first);
const auto reaction = (i != end(_all)) ? i->second : _shownReaction;
return createRow(peer, reaction);
}
return createRow(peer, _shownReaction);
}
std::unique_ptr<PeerListState> Controller::saveState() const {
auto result = PeerListController::saveState();
auto my = std::make_unique<SavedState>();
my->shownReaction = _shownReaction;
my->idsMap = _idsMap;
my->idsCounter = _idsCounter;
my->all = _all;
my->allOffset = _allOffset;
my->filtered = _filtered;
my->filteredOffset = _filteredOffset;
my->wasLoading = (_loadRequestId != 0);
result->controllerState = std::move(my);
return result;
}
void Controller::restoreState(std::unique_ptr<PeerListState> state) {
auto typeErasedState = state
? state->controllerState.get()
: nullptr;
if (const auto my = dynamic_cast<SavedState*>(typeErasedState)) {
if (const auto requestId = base::take(_loadRequestId)) {
_api.request(requestId).cancel();
}
_shownReaction = my->shownReaction;
_idsMap = std::move(my->idsMap);
_idsCounter = my->idsCounter;
_all = std::move(my->all);
_allOffset = std::move(my->allOffset);
_filtered = std::move(my->filtered);
_filteredOffset = std::move(my->filteredOffset);
if (my->wasLoading) {
loadMoreRows();
}
PeerListController::restoreState(std::move(state));
if (delegate()->peerListFullRowsCount()) {
setDescriptionText(QString());
delegate()->peerListRefreshRows();
}
}
}
void Controller::loadMore(const ReactionId &reaction) {
if (reaction.emoji() == u"read"_q) {
loadMore(ReactionId());
@@ -290,8 +372,8 @@ void Controller::loadMore(const ReactionId &reaction) {
| (reaction.empty() ? Flag(0) : Flag::f_reaction);
_loadRequestId = _api.request(MTPmessages_GetMessageReactionsList(
MTP_flags(flags),
_item->history()->peer->input,
MTP_int(_item->id),
_peer->input,
MTP_int(_itemId.msg),
Data::ReactionToMTP(reaction),
MTP_string(offset),
MTP_int(offset.isEmpty() ? kPerPageFirst : kPerPage)
@@ -332,7 +414,7 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
const auto window = _window;
const auto peer = row->peer();
crl::on_main(window, [=] {
window->show(PrepareShortInfoBox(peer, window));
window->showPeerInfo(peer);
});
}
@@ -353,72 +435,75 @@ std::unique_ptr<PeerListRow> Controller::createRow(
_factory,
Data::ReactionEntityData(reaction),
[=](Row *row) { delegate()->peerListUpdateRow(row); },
[=] { return _window->isGifPausedAtLeastFor(
[=] { return _window->parentController()->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer); });
}
} // namespace
object_ptr<Ui::BoxContent> FullListBox(
not_null<Window::SessionController*> window,
Data::ReactionId DefaultSelectedTab(
not_null<HistoryItem*> item,
std::shared_ptr<Api::WhoReadList> whoReadIds) {
return DefaultSelectedTab(item, {}, std::move(whoReadIds));
}
Data::ReactionId DefaultSelectedTab(
not_null<HistoryItem*> item,
Data::ReactionId selected,
std::shared_ptr<Api::WhoReadList> whoReadIds) {
Expects(IsServerMsgId(item->id));
if (!ranges::contains(
item->reactions(),
selected,
&Data::MessageReaction::id)) {
const auto proj = &Data::MessageReaction::id;
if (!ranges::contains(item->reactions(), selected, proj)) {
selected = {};
}
if (selected.empty() && whoReadIds && !whoReadIds->list.empty()) {
selected = Data::ReactionId{ u"read"_q };
}
const auto tabRequests = std::make_shared<
rpl::event_stream<Data::ReactionId>>();
const auto initBox = [=](not_null<PeerListBox*> box) {
box->setNoContentMargin(true);
return (selected.empty() && whoReadIds && !whoReadIds->list.empty())
? Data::ReactionId{ u"read"_q }
: selected;
}
auto map = item->reactions();
if (whoReadIds && !whoReadIds->list.empty()) {
map.push_back({
.id = Data::ReactionId{ u"read"_q },
.count = int(whoReadIds->list.size()),
});
}
const auto tabs = CreateTabs(
box,
Data::ReactedMenuFactory(&item->history()->session()),
[=] { return window->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer); },
map,
selected,
whoReadIds ? whoReadIds->type : Ui::WhoReadType::Reacted);
tabs->changes(
) | rpl::start_to_stream(*tabRequests, box->lifetime());
box->widthValue(
) | rpl::start_with_next([=](int width) {
tabs->resizeToWidth(width);
tabs->move(0, 0);
}, box->lifetime());
tabs->heightValue(
) | rpl::start_with_next([=](int height) {
box->setAddedTopScrollSkip(height);
}, box->lifetime());
box->addButton(tr::lng_close(), [=] {
box->closeBox();
not_null<Tabs*> CreateReactionsTabs(
not_null<QWidget*> parent,
not_null<Window::SessionNavigation*> window,
FullMsgId itemId,
Data::ReactionId selected,
std::shared_ptr<Api::WhoReadList> whoReadIds) {
const auto item = window->session().data().message(itemId);
auto map = item
? item->reactions()
: std::vector<Data::MessageReaction>();
if (whoReadIds && !whoReadIds->list.empty()) {
map.push_back({
.id = Data::ReactionId{ u"read"_q },
.count = int(whoReadIds->list.size()),
});
};
return Box<PeerListBox>(
std::make_unique<Controller>(
}
return CreateTabs(
parent,
Data::ReactedMenuFactory(&window->session()),
[=] { return window->parentController()->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer); },
map,
selected,
whoReadIds ? whoReadIds->type : Ui::WhoReadType::Reacted);
}
PreparedFullList FullListController(
not_null<Window::SessionNavigation*> window,
FullMsgId itemId,
Data::ReactionId selected,
std::shared_ptr<Api::WhoReadList> whoReadIds) {
Expects(IsServerMsgId(itemId.msg));
const auto tab = std::make_shared<
rpl::event_stream<Data::ReactionId>>();
return {
.controller = std::make_unique<Controller>(
window,
item,
itemId,
selected,
tabRequests->events(),
tab->events(),
whoReadIds),
initBox);
.switchTab = [=](Data::ReactionId id) { tab->fire_copy(id); },
};
}
} // namespace HistoryView::Reactions

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/object_ptr.h"
class HistoryItem;
class PeerListController;
namespace Data {
struct ReactionId;
@@ -21,6 +22,7 @@ struct WhoReadList;
namespace Window {
class SessionController;
class SessionNavigation;
} // namespace Window
namespace Ui {
@@ -29,10 +31,31 @@ class BoxContent;
namespace HistoryView::Reactions {
object_ptr<Ui::BoxContent> FullListBox(
not_null<Window::SessionController*> window,
[[nodiscard]] Data::ReactionId DefaultSelectedTab(
not_null<HistoryItem*> item,
std::shared_ptr<Api::WhoReadList> whoReadIds);
[[nodiscard]] Data::ReactionId DefaultSelectedTab(
not_null<HistoryItem*> item,
Data::ReactionId selected,
std::shared_ptr<Api::WhoReadList> whoReadIds = nullptr);
struct Tabs;
[[nodiscard]] not_null<Tabs*> CreateReactionsTabs(
not_null<QWidget*> parent,
not_null<Window::SessionNavigation*> window,
FullMsgId itemId,
Data::ReactionId selected,
std::shared_ptr<Api::WhoReadList> whoReadIds);
struct PreparedFullList {
std::unique_ptr<PeerListController> controller;
Fn<void(Data::ReactionId)> switchTab;
};
[[nodiscard]] PreparedFullList FullListController(
not_null<Window::SessionNavigation*> window,
FullMsgId itemId,
Data::ReactionId selected,
std::shared_ptr<Api::WhoReadList> whoReadIds = nullptr);
} // namespace HistoryView::Reactions

View File

@@ -27,7 +27,7 @@ struct Tabs {
Fn<rpl::producer<int>()> heightValue;
};
not_null<Tabs*> CreateTabs(
[[nodiscard]] not_null<Tabs*> CreateTabs(
not_null<QWidget*> parent,
Ui::Text::CustomEmojiFactory factory,
Fn<bool()> paused,

View File

@@ -240,7 +240,7 @@ void InnerWidget::fill() {
),
rpl::duplicate(availableBalanceValue),
rpl::duplicate(dateValue),
std::move(dateValue) | rpl::map([=](const QDateTime &dt) {
rpl::duplicate(dateValue) | rpl::map([=](const QDateTime &dt) {
return !dt.isNull() || (!_state.isWithdrawalEnabled);
}),
rpl::duplicate(availableBalanceValue) | rpl::map([=](uint64 v) {

View File

@@ -96,6 +96,10 @@ giveawayGiftCodeValue: FlatLabel(defaultFlatLabel) {
giveawayGiftCodeValueMultiline: FlatLabel(giveawayGiftCodeValue) {
minWidth: 128px;
maxHeight: 100px;
style: TextStyle(defaultTextStyle) {
font: font(10px);
linkUnderline: kLinkUnderlineNever;
}
}
giveawayGiftMessage: FlatLabel(giveawayGiftCodeValue) {
minWidth: 128px;

View File

@@ -35,9 +35,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/vertical_list.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/slider_natural_width.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/ui_utility.h"
#include "styles/style_dialogs.h" // dialogsSearchTabs
#include "styles/style_giveaway.h"
#include "styles/style_info.h"
#include "styles/style_premium.h"
@@ -430,7 +432,7 @@ void InnerWidget::fill() {
#else
const auto hasOneTab = (hasBoosts != hasGifts);
#endif
const auto boostsTabText = tr::lng_boosts_list_title(
const auto boostsTabText = tr::lng_giveaway_quantity(
tr::now,
lt_count,
status.firstSliceBoosts.multipliedTotal);
@@ -454,8 +456,19 @@ void InnerWidget::fill() {
inner,
object_ptr<Ui::CustomWidthSlider>(
inner,
st::defaultTabsSlider)),
st::dialogsSearchTabs)),
st::boxRowPadding);
if (const auto shadow = Ui::CreateChild<Ui::PlainShadow>(inner)) {
shadow->show();
slider->geometryValue(
) | rpl::start_with_next([=](const QRect &r) {
shadow->setGeometry(
inner->x(),
rect::bottom(r) - shadow->height(),
inner->width(),
shadow->height());
}, shadow->lifetime());
}
slider->toggle(!hasOneTab, anim::type::instant);
slider->entity()->addSection(boostsTabText);

View File

@@ -119,27 +119,6 @@ void ShowMenu(not_null<Ui::GenericBox*> box, const QString &text) {
});
}
void AddArrow(not_null<Ui::RpWidget*> parent) {
const auto arrow = Ui::CreateChild<Ui::RpWidget>(parent.get());
arrow->paintRequest(
) | rpl::start_with_next([=](const QRect &r) {
auto p = QPainter(arrow);
const auto path = Ui::ToggleUpDownArrowPath(
st::statisticsShowMoreButtonArrowSize,
st::statisticsShowMoreButtonArrowSize,
st::statisticsShowMoreButtonArrowSize,
st::mainMenuToggleFourStrokes,
0.);
auto hq = PainterHighQualityEnabler(p);
p.fillPath(path, st::lightButtonFg);
}, arrow->lifetime());
arrow->resize(Size(st::statisticsShowMoreButtonArrowSize * 2));
arrow->move(st::statisticsShowMoreButtonArrowPosition);
arrow->show();
}
void AddHeader(
not_null<Ui::VerticalLayout*> content,
tr::phrase<> text) {
@@ -833,7 +812,7 @@ void InnerWidget::fill() {
Ui::AddSkip(container);
}
#ifndef _DEBUG
if (!channel->amCreator()) {
if (channel && !channel->amCreator()) {
Ui::AddSkip(container);
Ui::AddSkip(container);
return;
@@ -1346,8 +1325,8 @@ void InnerWidget::fill() {
handleSlice(firstSlice);
if (!firstSlice.allLoaded) {
struct ShowMoreState final {
ShowMoreState(not_null<ChannelData*> channel)
: api(channel) {
ShowMoreState(not_null<PeerData*> peer)
: api(peer) {
}
Api::EarnStatistics api;
bool loading = false;
@@ -1355,7 +1334,7 @@ void InnerWidget::fill() {
rpl::variable<int> showed = 0;
};
const auto state
= lifetime().make_state<ShowMoreState>(channel);
= lifetime().make_state<ShowMoreState>(_peer);
state->token = firstSlice.token;
state->showed = firstSlice.list.size();
const auto max = firstSlice.total;
@@ -1372,7 +1351,7 @@ void InnerWidget::fill() {
) | tr::to_count()),
st::statisticsShowMoreButton)));
const auto button = wrap->entity();
AddArrow(button);
Ui::AddToggleUpDownArrowToMoreButton(button);
wrap->toggle(true, anim::type::instant);
const auto handleReceived = [=](

View File

@@ -470,6 +470,7 @@ infoIconMediaSaved: icon {{ "info/info_media_saved", infoIconFg }};
infoIconMediaStoriesArchive: icon {{ "info/info_stories_archive", infoIconFg }};
infoIconMediaStoriesRecent: icon {{ "info/info_stories_recent", infoIconFg }};
infoIconMediaGifts: icon {{ "menu/gift_premium", infoIconFg, point(4px, 4px) }};
infoIconEmojiStatusAccess: icon {{ "menu/read_reactions", infoIconFg, point(4px, 4px) }};
infoIconShare: icon {{ "info/info_share", infoIconFg }};
infoIconEdit: icon {{ "info/info_edit", infoIconFg }};

View File

@@ -7,27 +7,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "info/info_content_widget.h"
#include "window/window_session_controller.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/search_field_controller.h"
#include "ui/ui_utility.h"
#include "lang/lang_keys.h"
#include "info/profile/info_profile_widget.h"
#include "info/media/info_media_widget.h"
#include "info/common_groups/info_common_groups_widget.h"
#include "info/info_layer_widget.h"
#include "info/info_section_widget.h"
#include "info/info_controller.h"
#include "api/api_who_reacted.h"
#include "boxes/peer_list_box.h"
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "data/data_session.h"
#include "data/data_forum_topic.h"
#include "data/data_forum.h"
#include "info/profile/info_profile_widget.h"
#include "info/media/info_media_widget.h"
#include "info/common_groups/info_common_groups_widget.h"
#include "info/info_layer_widget.h"
#include "info/info_section_widget.h"
#include "info/info_controller.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/search_field_controller.h"
#include "ui/ui_utility.h"
#include "window/window_peer_menu.h"
#include "window/window_session_controller.h"
#include "styles/style_info.h"
#include "styles/style_profile.h"
#include "styles/style_layers.h"
@@ -377,6 +378,8 @@ Key ContentMemento::key() const {
return Stories::Tag{ peer, storiesTab() };
} else if (const auto peer = statisticsTag().peer) {
return statisticsTag();
} else if (const auto who = reactionsWhoReadIds()) {
return Key(who, _reactionsSelected, _pollReactionsContextId);
} else {
return Downloads::Tag();
}
@@ -417,4 +420,15 @@ ContentMemento::ContentMemento(Statistics::Tag statistics)
: _statisticsTag(statistics) {
}
ContentMemento::ContentMemento(
std::shared_ptr<Api::WhoReadList> whoReadIds,
FullMsgId contextId,
Data::ReactionId selected)
: _reactionsWhoReadIds(whoReadIds
? whoReadIds
: std::make_shared<Api::WhoReadList>())
, _reactionsSelected(selected)
, _pollReactionsContextId(contextId) {
}
} // namespace Info

View File

@@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/info_wrap_widget.h"
#include "info/statistics/info_statistics_tag.h"
namespace Api {
struct WhoReadList;
} // namespace Api
namespace Dialogs::Stories {
struct Content;
} // namespace Dialogs::Stories
@@ -189,8 +193,12 @@ public:
explicit ContentMemento(Statistics::Tag statistics);
ContentMemento(not_null<PollData*> poll, FullMsgId contextId)
: _poll(poll)
, _pollContextId(contextId) {
, _pollReactionsContextId(contextId) {
}
ContentMemento(
std::shared_ptr<Api::WhoReadList> whoReadIds,
FullMsgId contextId,
Data::ReactionId selected);
virtual object_ptr<ContentWidget> createWidget(
QWidget *parent,
@@ -222,7 +230,16 @@ public:
return _poll;
}
FullMsgId pollContextId() const {
return _pollContextId;
return _poll ? _pollReactionsContextId : FullMsgId();
}
std::shared_ptr<Api::WhoReadList> reactionsWhoReadIds() const {
return _reactionsWhoReadIds;
}
Data::ReactionId reactionsSelected() const {
return _reactionsSelected;
}
FullMsgId reactionsContextId() const {
return _reactionsWhoReadIds ? _pollReactionsContextId : FullMsgId();
}
Key key() const;
@@ -264,7 +281,9 @@ private:
Stories::Tab _storiesTab = {};
Statistics::Tag _statisticsTag;
PollData * const _poll = nullptr;
const FullMsgId _pollContextId;
std::shared_ptr<Api::WhoReadList> _reactionsWhoReadIds;
Data::ReactionId _reactionsSelected;
const FullMsgId _pollReactionsContextId;
int _scrollTop = 0;
QString _searchFieldQuery;

View File

@@ -50,6 +50,13 @@ Key::Key(not_null<PollData*> poll, FullMsgId contextId)
: _value(PollKey{ poll, contextId }) {
}
Key::Key(
std::shared_ptr<Api::WhoReadList> whoReadIds,
Data::ReactionId selected,
FullMsgId contextId)
: _value(ReactionsKey{ whoReadIds, selected, contextId }) {
}
PeerData *Key::peer() const {
if (const auto peer = std::get_if<not_null<PeerData*>>(&_value)) {
return *peer;
@@ -113,6 +120,27 @@ FullMsgId Key::pollContextId() const {
return FullMsgId();
}
std::shared_ptr<Api::WhoReadList> Key::reactionsWhoReadIds() const {
if (const auto data = std::get_if<ReactionsKey>(&_value)) {
return data->whoReadIds;
}
return nullptr;
}
Data::ReactionId Key::reactionsSelected() const {
if (const auto data = std::get_if<ReactionsKey>(&_value)) {
return data->selected;
}
return Data::ReactionId();
}
FullMsgId Key::reactionsContextId() const {
if (const auto data = std::get_if<ReactionsKey>(&_value)) {
return data->contextId;
}
return FullMsgId();
}
rpl::producer<SparseIdsMergedSlice> AbstractController::mediaSource(
SparseIdsMergedSlice::UniversalMsgId aroundId,
int limitBefore,
@@ -183,6 +211,19 @@ PollData *AbstractController::poll() const {
return nullptr;
}
auto AbstractController::reactionsWhoReadIds() const
-> std::shared_ptr<Api::WhoReadList> {
return key().reactionsWhoReadIds();
}
Data::ReactionId AbstractController::reactionsSelected() const {
return key().reactionsSelected();
}
FullMsgId AbstractController::reactionsContextId() const {
return key().reactionsContextId();
}
void AbstractController::showSection(
std::shared_ptr<Window::SectionMemento> memento,
const Window::SectionShow &params) {
@@ -296,6 +337,7 @@ void Controller::updateSearchControllers(
: Section::MediaType::kCount;
const auto hasMediaSearch = isMedia
&& SharedMediaAllowSearch(mediaType);
const auto hasRequestsListSearch = (type == Type::RequestsList);
const auto hasCommonGroupsSearch = (type == Type::CommonGroups);
const auto hasDownloadsSearch = (type == Type::Downloads);
const auto hasMembersSearch = (type == Type::Members)
@@ -312,6 +354,7 @@ void Controller::updateSearchControllers(
_searchController = nullptr;
}
if (hasMediaSearch
|| hasRequestsListSearch
|| hasCommonGroupsSearch
|| hasDownloadsSearch
|| hasMembersSearch) {

View File

@@ -7,10 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_message_reaction_id.h"
#include "data/data_search_controller.h"
#include "info/statistics/info_statistics_tag.h"
#include "window/window_session_controller.h"
namespace Api {
struct WhoReadList;
} // namespace Api
namespace Data {
class ForumTopic;
} // namespace Data
@@ -67,6 +72,10 @@ public:
Key(Stories::Tag stories);
Key(Statistics::Tag statistics);
Key(not_null<PollData*> poll, FullMsgId contextId);
Key(
std::shared_ptr<Api::WhoReadList> whoReadIds,
Data::ReactionId selected,
FullMsgId contextId);
PeerData *peer() const;
Data::ForumTopic *topic() const;
@@ -77,12 +86,20 @@ public:
Statistics::Tag statisticsTag() const;
PollData *poll() const;
FullMsgId pollContextId() const;
std::shared_ptr<Api::WhoReadList> reactionsWhoReadIds() const;
Data::ReactionId reactionsSelected() const;
FullMsgId reactionsContextId() const;
private:
struct PollKey {
not_null<PollData*> poll;
FullMsgId contextId;
};
struct ReactionsKey {
std::shared_ptr<Api::WhoReadList> whoReadIds;
Data::ReactionId selected;
FullMsgId contextId;
};
std::variant<
not_null<PeerData*>,
not_null<Data::ForumTopic*>,
@@ -90,7 +107,8 @@ private:
Downloads::Tag,
Stories::Tag,
Statistics::Tag,
PollKey> _value;
PollKey,
ReactionsKey> _value;
};
@@ -106,6 +124,8 @@ public:
Media,
CommonGroups,
SimilarChannels,
RequestsList,
ReactionsList,
SavedSublists,
PeerGifts,
Members,
@@ -186,6 +206,10 @@ public:
[[nodiscard]] FullMsgId pollContextId() const {
return key().pollContextId();
}
[[nodiscard]] auto reactionsWhoReadIds() const
-> std::shared_ptr<Api::WhoReadList>;
[[nodiscard]] Data::ReactionId reactionsSelected() const;
[[nodiscard]] FullMsgId reactionsContextId() const;
virtual void setSearchEnabledByContent(bool enabled) {
}

View File

@@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/saved/info_saved_sublists_widget.h"
#include "info/settings/info_settings_widget.h"
#include "info/similar_channels/info_similar_channels_widget.h"
#include "info/reactions_list/info_reactions_list_widget.h"
#include "info/requests_list/info_requests_list_widget.h"
#include "info/peer_gifts/info_peer_gifts_widget.h"
#include "info/polls/info_polls_results_widget.h"
#include "info/info_section_widget.h"
@@ -53,6 +55,13 @@ Memento::Memento(not_null<PollData*> poll, FullMsgId contextId)
: Memento(DefaultStack(poll, contextId)) {
}
Memento::Memento(
std::shared_ptr<Api::WhoReadList> whoReadIds,
FullMsgId contextId,
Data::ReactionId selected)
: Memento(DefaultStack(std::move(whoReadIds), contextId, selected)) {
}
Memento::Memento(std::vector<std::shared_ptr<ContentMemento>> stack)
: _stack(std::move(stack)) {
auto topics = base::flat_set<not_null<Data::ForumTopic*>>();
@@ -112,6 +121,18 @@ std::vector<std::shared_ptr<ContentMemento>> Memento::DefaultStack(
return result;
}
std::vector<std::shared_ptr<ContentMemento>> Memento::DefaultStack(
std::shared_ptr<Api::WhoReadList> whoReadIds,
FullMsgId contextId,
Data::ReactionId selected) {
auto result = std::vector<std::shared_ptr<ContentMemento>>();
result.push_back(std::make_shared<ReactionsList::Memento>(
std::move(whoReadIds),
contextId,
selected));
return result;
}
Section Memento::DefaultSection(not_null<PeerData*> peer) {
if (peer->savedSublistsInfo()) {
return Section(Section::Type::SavedSublists);
@@ -149,6 +170,8 @@ std::shared_ptr<ContentMemento> Memento::DefaultContent(
case Section::Type::SimilarChannels:
return std::make_shared<SimilarChannels::Memento>(
peer->asChannel());
case Section::Type::RequestsList:
return std::make_shared<RequestsList::Memento>(peer->asChannel());
case Section::Type::PeerGifts:
return std::make_shared<PeerGifts::Memento>(peer->asUser());
case Section::Type::SavedSublists:

View File

@@ -13,12 +13,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/section_memento.h"
#include "base/object_ptr.h"
namespace Api {
struct WhoReadList;
} // namespace Api
namespace Storage {
enum class SharedMediaType : signed char;
} // namespace Storage
namespace Data {
class ForumTopic;
struct ReactionId;
} // namespace Data
namespace Ui {
@@ -46,6 +51,10 @@ public:
Memento(not_null<Data::ForumTopic*> topic, Section section);
Memento(Settings::Tag settings, Section section);
Memento(not_null<PollData*> poll, FullMsgId contextId);
Memento(
std::shared_ptr<Api::WhoReadList> whoReadIds,
FullMsgId contextId,
Data::ReactionId selected);
explicit Memento(std::vector<std::shared_ptr<ContentMemento>> stack);
object_ptr<Window::SectionWidget> createWidget(
@@ -91,6 +100,10 @@ private:
static std::vector<std::shared_ptr<ContentMemento>> DefaultStack(
not_null<PollData*> poll,
FullMsgId contextId);
static std::vector<std::shared_ptr<ContentMemento>> DefaultStack(
std::shared_ptr<Api::WhoReadList> whoReadIds,
FullMsgId contextId,
Data::ReactionId selected);
static std::shared_ptr<ContentMemento> DefaultContent(
not_null<PeerData*> peer,

View File

@@ -24,8 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_layers.h"
#include "styles/style_info.h"
namespace Info {
namespace Polls {
namespace Info::Polls {
namespace {
constexpr auto kFirstPage = 15;
@@ -659,6 +658,4 @@ auto InnerWidget::showPeerInfoRequests() const
return _showPeerInfoRequests.events();
}
} // namespace Polls
} // namespace Info
} // namespace Info::Polls

View File

@@ -16,10 +16,10 @@ class VerticalLayout;
} // namespace Ui
namespace Info {
class Controller;
} // namespace Info
namespace Polls {
namespace Info::Polls {
class Memento;
class ListController;
@@ -70,5 +70,4 @@ private:
};
} // namespace Polls
} // namespace Info
} // namespace Info::Polls

View File

@@ -13,8 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_poll.h"
#include "ui/ui_utility.h"
namespace Info {
namespace Polls {
namespace Info::Polls {
Memento::Memento(not_null<PollData*> poll, FullMsgId contextId)
: ContentMemento(poll, contextId) {
@@ -113,5 +112,4 @@ void Widget::restoreState(not_null<Memento*> memento) {
scrollTopRestore(memento->scrollTop());
}
} // namespace Polls
} // namespace Info
} // namespace Info::Polls

View File

@@ -12,8 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
struct PeerListState;
namespace Info {
namespace Polls {
namespace Info::Polls {
class InnerWidget;
@@ -68,5 +67,4 @@ private:
};
} // namespace Polls
} // namespace Info
} // namespace Info::Polls

View File

@@ -958,6 +958,7 @@ private:
object_ptr<Ui::RpWidget> setupInfo();
object_ptr<Ui::RpWidget> setupMuteToggle();
void setupMainApp();
void setupBotPermissions();
void setupMainButtons();
Ui::MultiSlideTracker fillTopicButtons();
Ui::MultiSlideTracker fillUserButtons(
@@ -1865,6 +1866,37 @@ void DetailsFiller::setupMainApp() {
Ui::AddSkip(_wrap);
}
void DetailsFiller::setupBotPermissions() {
AddSkip(_wrap);
AddSubsectionTitle(_wrap, tr::lng_profile_bot_permissions_title());
const auto emoji = _wrap->add(
object_ptr<Ui::SettingsButton>(
_wrap,
tr::lng_profile_bot_emoji_status_access(),
st::infoSharedMediaButton));
object_ptr<Profile::FloatingIcon>(
emoji,
st::infoIconEmojiStatusAccess,
st::infoSharedMediaButtonIconPosition);
const auto user = _peer->asUser();
emoji->toggleOn(
rpl::single(bool(user->botInfo->canManageEmojiStatus))
)->toggledValue() | rpl::filter([=](bool allowed) {
return allowed != user->botInfo->canManageEmojiStatus;
}) | rpl::start_with_next([=](bool allowed) {
user->botInfo->canManageEmojiStatus = allowed;
const auto session = &user->session();
session->api().request(MTPbots_ToggleUserEmojiStatusPermission(
user->inputUser,
MTP_bool(allowed)
)).send();
}, emoji->lifetime());
AddSkip(_wrap);
AddDivider(_wrap);
AddSkip(_wrap);
}
void DetailsFiller::setupMainButtons() {
auto wrapButtons = [=](auto &&callback) {
auto topSkip = _wrap->add(CreateSlideSkipWidget(_wrap));
@@ -2059,6 +2091,9 @@ object_ptr<Ui::RpWidget> DetailsFiller::fill() {
if (info->hasMainApp) {
setupMainApp();
}
if (info->canManageEmojiStatus) {
setupBotPermissions();
}
}
}
if (!_peer->isSelf()) {

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