diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 162b6224d..4961c68f3 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -41,7 +41,7 @@ on:
jobs:
linux:
- name: CentOS 7
+ name: Rocky Linux 8
runs-on: ubuntu-latest
container:
image: ghcr.io/${{ github.repository }}/centos_env
@@ -51,7 +51,7 @@ jobs:
defaults:
run:
- shell: scl enable rh-python38 -- scl enable llvm-toolset-7.0 -- scl enable devtoolset-10 -- bash --noprofile --norc -eo pipefail {0}
+ shell: scl enable gcc-toolset-12 -- bash --noprofile --norc -eo pipefail {0}
strategy:
matrix:
diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml
index 4402a95f0..f924fe7a5 100644
--- a/.github/workflows/win.yml
+++ b/.github/workflows/win.yml
@@ -124,7 +124,7 @@ jobs:
echo "TDESKTOP_BUILD_DEFINE=$DEFINE" >> $GITHUB_ENV
API="-D TDESKTOP_API_TEST=ON"
- if [ ${{ github.ref == 'refs/heads/nightly' }} ]; then
+ if [ $GITHUB_REF == 'refs/heads/nightly' ]; then
echo "Use the open credentials."
API="-D TDESKTOP_API_ID=611335 -D TDESKTOP_API_HASH=d524b414d21f4d37f08684c1df41ac9c"
fi
diff --git a/.gitmodules b/.gitmodules
index 333aa6d9e..47d501392 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -58,9 +58,6 @@
[submodule "Telegram/ThirdParty/range-v3"]
path = Telegram/ThirdParty/range-v3
url = https://github.com/ericniebler/range-v3.git
-[submodule "Telegram/ThirdParty/fcitx-qt5"]
- path = Telegram/ThirdParty/fcitx-qt5
- url = https://github.com/fcitx/fcitx-qt5.git
[submodule "Telegram/ThirdParty/nimf"]
path = Telegram/ThirdParty/nimf
url = https://github.com/hamonikr/nimf.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4beb508c0..8457b36d8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -61,7 +61,7 @@ if (NOT DESKTOP_APP_USE_PACKAGED)
if (WIN32)
set(qt_version 5.15.10)
elseif (APPLE)
- set(qt_version 6.3.2)
+ set(qt_version 6.2.5)
endif()
endif()
include(cmake/external/qt/package.cmake)
diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 9bdfbc9b6..d2e79c54f 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -478,6 +478,7 @@ PRIVATE
core/crash_report_window.h
core/crash_reports.cpp
core/crash_reports.h
+ core/deadlock_detector.h
core/file_utilities.cpp
core/file_utilities.h
core/launcher.cpp
@@ -1541,6 +1542,7 @@ PRIVATE
qrc/emoji_5.qrc
qrc/emoji_6.qrc
qrc/emoji_7.qrc
+ qrc/emoji_8.qrc
qrc/emoji_preview.qrc
qrc/telegram/animations.qrc
qrc/telegram/export.qrc
diff --git a/Telegram/Resources/emoji/emoji_1.webp b/Telegram/Resources/emoji/emoji_1.webp
index c4051383a..4bf92eca5 100644
Binary files a/Telegram/Resources/emoji/emoji_1.webp and b/Telegram/Resources/emoji/emoji_1.webp differ
diff --git a/Telegram/Resources/emoji/emoji_2.webp b/Telegram/Resources/emoji/emoji_2.webp
index c707db8e9..988947793 100644
Binary files a/Telegram/Resources/emoji/emoji_2.webp and b/Telegram/Resources/emoji/emoji_2.webp differ
diff --git a/Telegram/Resources/emoji/emoji_3.webp b/Telegram/Resources/emoji/emoji_3.webp
index d7eddeb5c..5e2172deb 100644
Binary files a/Telegram/Resources/emoji/emoji_3.webp and b/Telegram/Resources/emoji/emoji_3.webp differ
diff --git a/Telegram/Resources/emoji/emoji_4.webp b/Telegram/Resources/emoji/emoji_4.webp
index b668f059b..4859c1337 100644
Binary files a/Telegram/Resources/emoji/emoji_4.webp and b/Telegram/Resources/emoji/emoji_4.webp differ
diff --git a/Telegram/Resources/emoji/emoji_5.webp b/Telegram/Resources/emoji/emoji_5.webp
index 064a94d3f..3539ebe03 100644
Binary files a/Telegram/Resources/emoji/emoji_5.webp and b/Telegram/Resources/emoji/emoji_5.webp differ
diff --git a/Telegram/Resources/emoji/emoji_6.webp b/Telegram/Resources/emoji/emoji_6.webp
index fe7d47564..8da39a6b1 100644
Binary files a/Telegram/Resources/emoji/emoji_6.webp and b/Telegram/Resources/emoji/emoji_6.webp differ
diff --git a/Telegram/Resources/emoji/emoji_7.webp b/Telegram/Resources/emoji/emoji_7.webp
index 970a64c95..3c31b2345 100644
Binary files a/Telegram/Resources/emoji/emoji_7.webp and b/Telegram/Resources/emoji/emoji_7.webp differ
diff --git a/Telegram/Resources/emoji/emoji_8.webp b/Telegram/Resources/emoji/emoji_8.webp
new file mode 100644
index 000000000..c3e6b3b0d
Binary files /dev/null and b/Telegram/Resources/emoji/emoji_8.webp differ
diff --git a/Telegram/Resources/icons/limits/boost.png b/Telegram/Resources/icons/limits/boost.png
new file mode 100644
index 000000000..b2c8bc7a0
Binary files /dev/null and b/Telegram/Resources/icons/limits/boost.png differ
diff --git a/Telegram/Resources/icons/limits/boost@2x.png b/Telegram/Resources/icons/limits/boost@2x.png
new file mode 100644
index 000000000..a4c54adcd
Binary files /dev/null and b/Telegram/Resources/icons/limits/boost@2x.png differ
diff --git a/Telegram/Resources/icons/limits/boost@3x.png b/Telegram/Resources/icons/limits/boost@3x.png
new file mode 100644
index 000000000..194195fdb
Binary files /dev/null and b/Telegram/Resources/icons/limits/boost@3x.png differ
diff --git a/Telegram/Resources/icons/mediaview/views.png b/Telegram/Resources/icons/mediaview/views.png
new file mode 100644
index 000000000..e8496be33
Binary files /dev/null and b/Telegram/Resources/icons/mediaview/views.png differ
diff --git a/Telegram/Resources/icons/mediaview/views@2x.png b/Telegram/Resources/icons/mediaview/views@2x.png
new file mode 100644
index 000000000..30b516900
Binary files /dev/null and b/Telegram/Resources/icons/mediaview/views@2x.png differ
diff --git a/Telegram/Resources/icons/mediaview/views@3x.png b/Telegram/Resources/icons/mediaview/views@3x.png
new file mode 100644
index 000000000..523f11cc8
Binary files /dev/null and b/Telegram/Resources/icons/mediaview/views@3x.png differ
diff --git a/Telegram/Resources/icons/mini_forward.png b/Telegram/Resources/icons/mini_forward.png
index 0b12af15d..e51712b55 100644
Binary files a/Telegram/Resources/icons/mini_forward.png and b/Telegram/Resources/icons/mini_forward.png differ
diff --git a/Telegram/Resources/icons/mini_forward@2x.png b/Telegram/Resources/icons/mini_forward@2x.png
index c8194ef00..8a8b8dd57 100644
Binary files a/Telegram/Resources/icons/mini_forward@2x.png and b/Telegram/Resources/icons/mini_forward@2x.png differ
diff --git a/Telegram/Resources/icons/mini_forward@3x.png b/Telegram/Resources/icons/mini_forward@3x.png
index 19cac4700..f6ca40a23 100644
Binary files a/Telegram/Resources/icons/mini_forward@3x.png and b/Telegram/Resources/icons/mini_forward@3x.png differ
diff --git a/Telegram/Resources/icons/mini_reply_story.png b/Telegram/Resources/icons/mini_reply_story.png
index bfcbc0c48..d1b436beb 100644
Binary files a/Telegram/Resources/icons/mini_reply_story.png and b/Telegram/Resources/icons/mini_reply_story.png differ
diff --git a/Telegram/Resources/icons/mini_reply_story@2x.png b/Telegram/Resources/icons/mini_reply_story@2x.png
index a3c75e8bf..8eddfb9ef 100644
Binary files a/Telegram/Resources/icons/mini_reply_story@2x.png and b/Telegram/Resources/icons/mini_reply_story@2x.png differ
diff --git a/Telegram/Resources/icons/mini_reply_story@3x.png b/Telegram/Resources/icons/mini_reply_story@3x.png
index 471cfa57d..e836bcd3d 100644
Binary files a/Telegram/Resources/icons/mini_reply_story@3x.png and b/Telegram/Resources/icons/mini_reply_story@3x.png differ
diff --git a/Telegram/Resources/icons/stories/boost_mini.png b/Telegram/Resources/icons/stories/boost_mini.png
new file mode 100644
index 000000000..90a24d8f1
Binary files /dev/null and b/Telegram/Resources/icons/stories/boost_mini.png differ
diff --git a/Telegram/Resources/icons/stories/boost_mini@2x.png b/Telegram/Resources/icons/stories/boost_mini@2x.png
new file mode 100644
index 000000000..fd746a03a
Binary files /dev/null and b/Telegram/Resources/icons/stories/boost_mini@2x.png differ
diff --git a/Telegram/Resources/icons/stories/boost_mini@3x.png b/Telegram/Resources/icons/stories/boost_mini@3x.png
new file mode 100644
index 000000000..637a5716d
Binary files /dev/null and b/Telegram/Resources/icons/stories/boost_mini@3x.png differ
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 5592445c5..ac7e1e359 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -548,6 +548,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_title_account_name" = "Show active account";
"lng_settings_title_total_count" = "Total unread count";
"lng_settings_native_frame" = "Use system window frame";
+"lng_settings_qt_frame" = "Use Qt window frame";
"lng_settings_auto_start" = "Launch Telegram when system starts";
"lng_settings_start_min" = "Launch minimized";
"lng_settings_auto_start_disabled_uwp" = "Starting with the system was disabled in Windows Settings.\n\nPlease enable Telegram Desktop in the Startup Apps Settings.";
@@ -1182,6 +1183,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_loading" = "Loading...";
"lng_profile_saved_stories#one" = "{count} saved story";
"lng_profile_saved_stories#other" = "{count} saved stories";
+"lng_profile_posts#one" = "{count} post";
+"lng_profile_posts#other" = "{count} posts";
"lng_profile_photos#one" = "{count} photo";
"lng_profile_photos#other" = "{count} photos";
"lng_profile_gifs#one" = "{count} GIF";
@@ -1611,6 +1614,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_suggested_video" = "{user} suggests you to use this profile video.";
"lng_action_suggested_video_button" = "View Video";
"lng_action_attach_menu_bot_allowed" = "You allowed this bot to message you when you added it in the attachment menu.";
+"lng_action_webapp_bot_allowed" = "You allowed this bot to message you in his web-app.";
"lng_action_set_wallpaper_me" = "You set a new wallpaper for this chat";
"lng_action_set_wallpaper" = "{user} set a new wallpaper for this chat";
"lng_action_set_wallpaper_button" = "View Wallpaper";
@@ -1799,6 +1803,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_bot_share_location_unavailable" = "Sorry, location sharing is currently unavailable in Telegram Desktop.";
"lng_bot_share_phone" = "Do you want to share your phone number with this bot?";
"lng_bot_share_phone_confirm" = "Share";
+"lng_bot_allow_write_title" = "Allow messaging";
+"lng_bot_allow_write" = "Do you want to allow this bot writing you?";
+"lng_bot_allow_write_confirm" = "Allow";
"lng_attach_failed" = "Failed";
"lng_attach_file" = "File";
@@ -1995,6 +2002,38 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_premium_gift_terms" = "You can review the list of features and terms of use for Telegram Premium {link}.";
"lng_premium_gift_terms_link" = "here";
+"lng_boost_channel_button" = "Boost Channel";
+"lng_boost_level#one" = "Level {count}";
+"lng_boost_level#other" = "Level {count}";
+"lng_boost_channel_title_first" = "Enable stories for channel";
+"lng_boost_channel_needs_first#one" = "{channel} needs **{count}** more boost to enable posting stories. Help make it possible!";
+"lng_boost_channel_needs_first#other" = "{channel} needs **{count}** more boosts to enable posting stories. Help make it possible!";
+"lng_boost_channel_title_more" = "Help upgrade channel";
+"lng_boost_channel_needs_more#one" = "{channel} needs **{count}** more boost to be able to {post}.";
+"lng_boost_channel_needs_more#other" = "{channel} needs **{count}** more boosts to be able to {post}.";
+"lng_boost_channel_title_max" = "Maximum level reached";
+"lng_boost_channel_you_title" = "You boosted {channel}!";
+"lng_boost_channel_you_first#one" = "This channel needs **{count}** more boost\nto enable stories.";
+"lng_boost_channel_you_first#other" = "This channel needs **{count}** more boosts\nto enable stories.";
+"lng_boost_channel_you_more#one" = "This channel needs **{count}** more boost\nto be able to {post}.";
+"lng_boost_channel_you_more#other" = "This channel needs **{count}** more boosts\nto be able to {post}.";
+"lng_boost_channel_reached_first" = "This channel reached **Level 1** and can now post stories.";
+"lng_boost_channel_reached_more#one" = "This channel reached **Level {count}** and can now {post}.";
+"lng_boost_channel_reached_more#other" = "This channel reached **Level {count}** and can now {post}.";
+"lng_boost_channel_post_stories#one" = "post **{count} story** per day";
+"lng_boost_channel_post_stories#other" = "post **{count} stories** per day";
+"lng_boost_error_gifted_title" = "Can't boost with gifted Premium!";
+"lng_boost_error_gifted_text" = "Because your **Telegram Premium** subscription was gifted to you, you can't use it to boost channels.";
+"lng_boost_error_already_title" = "Already Boosted!";
+"lng_boost_error_already_text" = "You are already boosting this channel.";
+"lng_boost_error_premium_title" = "Premium needed!";
+"lng_boost_error_premium_text" = "Only **Telegram Premium** subscribers can boost channels. Do you want to subscribe to **Telegram Premium**?";
+"lng_boost_error_premium_yes" = "Yes";
+"lng_boost_error_flood_title" = "Can't boost too often!";
+"lng_boost_error_flood_text" = "You can change the channel you boost only once a day. Next time you can boost is in {left}.";
+"lng_boost_now_instead" = "You currently boost {channel}. Do you want to boost {other} instead?";
+"lng_boost_now_replace" = "Replace";
+
"lng_accounts_limit_title" = "Limit Reached";
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected accounts.";
"lng_accounts_limit1#other" = "You have reached the limit of **{count}** connected accounts.";
@@ -2256,17 +2295,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_bot_remove_from_menu" = "Remove From Menu";
"lng_bot_remove_from_menu_sure" = "Remove {bot} from the attachment menu?";
"lng_bot_remove_from_menu_done" = "Bot removed from the menu.";
+"lng_bot_remove_from_side_menu" = "Remove From Menu";
+"lng_bot_remove_from_side_menu_sure" = "Remove {bot} from the main menu?";
+"lng_bot_remove_from_side_menu_done" = "Bot removed from the main menu.";
"lng_bot_settings" = "Settings";
"lng_bot_open" = "Open Bot";
"lng_bot_reload_page" = "Reload Page";
"lng_bot_add_to_menu" = "{bot} asks your permission to be added as an option to your attachments menu so you can access it from any chat.";
"lng_bot_add_to_menu_done" = "Bot added to the menu.";
+"lng_bot_will_be_added" = "{bot} shortcuts will be added to the attachment options and the main menu.";
+"lng_bot_side_menu_new" = "NEW";
"lng_bot_menu_not_supported" = "This bot isn't supported in the attach menu.";
"lng_bot_menu_already_added" = "This bot is already added in your attach menu.";
"lng_bot_menu_button" = "Menu";
"lng_bot_close_warning_title" = "Warning";
"lng_bot_close_warning" = "Changes that you made may not be saved.";
"lng_bot_close_warning_sure" = "Close anyway";
+"lng_bot_add_to_side_menu" = "{bot} asks your permission to be added as an option to your main menu so you can access it any time.";
+"lng_bot_add_to_side_menu_done" = "Bot added to the main menu.";
"lng_typing" = "typing";
"lng_user_typing" = "{user} is typing";
@@ -2370,6 +2416,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_open_gif" = "Open GIF";
"lng_context_save_gif" = "Save GIF";
"lng_context_delete_gif" = "Delete GIF";
+"lng_context_open_channel" = "Open Channel";
"lng_context_attached_stickers" = "Attached Stickers";
"lng_context_to_msg" = "Go To Message";
"lng_context_reply_msg" = "Reply";
@@ -2747,6 +2794,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_payments_terms_title" = "Terms of Service";
"lng_payments_terms_text" = "Subscribe and accept terms of service of {bot}?";
+"lng_payments_terms_text_once" = "Are you accepting terms of service of {bot}?";
"lng_payments_terms_agree" = "I agree to {link}";
"lng_payments_terms_link" = "Terms of Service";
"lng_payments_terms_accept" = "Accept";
@@ -3079,9 +3127,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gigagroup_suggest_more" = "Learn more";
"lng_rights_channel_info" = "Change channel info";
+"lng_rights_channel_manage" = "Manage messages";
"lng_rights_channel_post" = "Post messages";
"lng_rights_channel_edit" = "Edit messages of others";
"lng_rights_channel_delete" = "Delete messages of others";
+"lng_rights_channel_manage_stories" = "Manage stories";
+"lng_rights_channel_post_stories" = "Post stories";
+"lng_rights_channel_edit_stories" = "Edit stories of others";
+"lng_rights_channel_delete_stories" = "Delete stories of others";
"lng_rights_channel_manage_calls" = "Manage live streams";
"lng_rights_group_info" = "Change group info";
"lng_rights_group_ban" = "Ban users";
@@ -3340,6 +3393,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_admin_log_admin_post_messages" = "Post messages";
"lng_admin_log_admin_edit_messages" = "Edit messages";
"lng_admin_log_admin_delete_messages" = "Delete messages";
+"lng_admin_log_admin_post_stories" = "Post stories";
+"lng_admin_log_admin_edit_stories" = "Edit stories";
+"lng_admin_log_admin_delete_stories" = "Delete stories";
"lng_admin_log_admin_remain_anonymous" = "Remain anonymous";
"lng_admin_log_admin_ban_users" = "Ban users";
"lng_admin_log_admin_invite_users" = "Add members";
@@ -3555,6 +3611,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_export_option_choose_format" = "Choose export format";
"lng_export_option_html" = "Human-readable HTML";
"lng_export_option_json" = "Machine-readable JSON";
+"lng_export_option_html_and_json" = "Both";
"lng_export_limits" = "From: {from}, to: {till}";
"lng_export_beginning" = "the oldest message";
"lng_export_end" = "present";
@@ -3819,6 +3876,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_view_button_voice_chat_channel" = "Live stream";
"lng_view_button_request_join" = "Request to Join";
"lng_view_button_external_link" = "Open link";
+"lng_view_button_boost" = "Boost";
"lng_sponsored_hide_ads" = "Hide";
"lng_sponsored_title" = "What are sponsored messages?";
@@ -3829,6 +3887,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_telegram_features_url" = "https://t.me/TelegramTips";
+"lng_mini_apps_disclaimer_title" = "Warning";
+"lng_mini_apps_disclaimer_text" = "You are about to use a mini app operated by an independent party **not affiliated with Telegram**. You must agree to the Terms of Use of mini apps to continue.";
+"lng_mini_apps_disclaimer_button" = "I agree to the {link}";
+"lng_mini_apps_disclaimer_link" = "Terms of Use";
+"lng_mini_apps_tos_url" = "https://telegram.org/tos/mini-apps";
+
"lng_ringtones_box_title" = "Notification Sound";
"lng_ringtones_box_cloud_subtitle" = "Choose your tone";
"lng_ringtones_box_upload_choose" = "Choose ringtone";
@@ -3946,6 +4010,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stories_recent_button" = "Recent Stories";
"lng_stories_archive_title" = "Stories Archive";
"lng_stories_archive_about" = "Only you can see archived stories unless you choose to save them to your profile.";
+"lng_stories_channel_archive_about" = "Only admins of the channel can see archived stories unless they are saved to the channel page.";
"lng_stories_reply_sent" = "Message Sent";
"lng_stories_hidden_to_contacts" = "Stories from {user} will now be shown in **Archived Chats**.";
"lng_stories_shown_in_chats" = "Stories from {user} will now be shown in the **Chats List**.";
@@ -3965,6 +4030,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stories_archive_done" = "This story is hidden from your profile.";
"lng_stories_archive_done_many#one" = "{count} story is hidden from your profile.";
"lng_stories_archive_done_many#other" = "{count} stories are hidden from your profile.";
+"lng_stories_channel_save_sure" = "Do you want to save this story to the channel page?";
+"lng_stories_channel_save_sure_many#one" = "Do you want to save {count} story to the channel page?";
+"lng_stories_channel_save_sure_many#other" = "Do you want to save {count} stories to the channel page?";
+"lng_stories_channel_save_done" = "This story is saved to the channel page.";
+"lng_stories_channel_save_done_many#one" = "{count} story is saved to the channel page.";
+"lng_stories_channel_save_done_many#other" = "{count} stories are saved to the channel page.";
+"lng_stories_channel_save_done_about" = "Saved stories can be viewed by others on the channel page until they are removed.";
+"lng_stories_channel_archive_sure" = "Do you want to hide this story from the channel page?";
+"lng_stories_channel_archive_sure_many#one" = "Do you want to hide {count} story from the channel page?";
+"lng_stories_channel_archive_sure_many#other" = "Do you want to hide {count} stories from the channel page?";
+"lng_stories_channel_archive_done" = "This story is hidden from the channel page.";
+"lng_stories_channel_archive_done_many#one" = "{count} story is hidden from the channel page.";
+"lng_stories_channel_archive_done_many#other" = "{count} stories are hidden from the channel page.";
"lng_stories_save_promo" = "Subscribe to {link} to download other people's unprotected stories to disk.";
"lng_stealth_mode_menu_item" = "Stealth Mode";
diff --git a/Telegram/Resources/qrc/emoji_8.qrc b/Telegram/Resources/qrc/emoji_8.qrc
new file mode 100644
index 000000000..51765a39b
--- /dev/null
+++ b/Telegram/Resources/qrc/emoji_8.qrc
@@ -0,0 +1,5 @@
+
+
+ ../emoji/emoji_8.webp
+
+
diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml
index 3b5ae67a8..65657e925 100644
--- a/Telegram/Resources/uwp/AppX/AppxManifest.xml
+++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml
@@ -10,7 +10,7 @@
+ Version="4.10.0.0" />
Telegram Desktop
Telegram Messenger LLP
diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc
index f0efceda4..b3a9340d4 100644
--- a/Telegram/Resources/winrc/Telegram.rc
+++ b/Telegram/Resources/winrc/Telegram.rc
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,9,4,0
- PRODUCTVERSION 4,9,4,0
+ FILEVERSION 4,10,0,0
+ PRODUCTVERSION 4,10,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop"
- VALUE "FileVersion", "4.9.4.0"
+ VALUE "FileVersion", "4.10.0.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "AyuGram Desktop"
- VALUE "ProductVersion", "4.9.4.0"
+ VALUE "ProductVersion", "4.10.0.0"
END
END
BLOCK "VarFileInfo"
diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc
index 5d8768e00..4b3956f9f 100644
--- a/Telegram/Resources/winrc/Updater.rc
+++ b/Telegram/Resources/winrc/Updater.rc
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,9,4,0
- PRODUCTVERSION 4,9,4,0
+ FILEVERSION 4,10,0,0
+ PRODUCTVERSION 4,10,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop Updater"
- VALUE "FileVersion", "4.9.4.0"
+ VALUE "FileVersion", "4.10.0.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "AyuGram Desktop"
- VALUE "ProductVersion", "4.9.4.0"
+ VALUE "ProductVersion", "4.10.0.0"
END
END
BLOCK "VarFileInfo"
diff --git a/Telegram/SourceFiles/_other/packer.cpp b/Telegram/SourceFiles/_other/packer.cpp
index 0ba79c1be..156ba726a 100644
--- a/Telegram/SourceFiles/_other/packer.cpp
+++ b/Telegram/SourceFiles/_other/packer.cpp
@@ -267,7 +267,7 @@ int main(int argc, char *argv[])
}
QByteArray inner = f.readAll();
stream << name << quint32(inner.size()) << inner;
-#ifdef Q_OS_UNIX
+#ifndef Q_OS_WIN
stream << (QFileInfo(fullName).isExecutable() ? true : false);
#endif
}
@@ -281,7 +281,7 @@ int main(int argc, char *argv[])
cout << "Compression start, size: " << resultSize << "\n";
QByteArray compressed, resultCheck;
-#if defined Q_OS_WIN && !defined DESKTOP_APP_USE_PACKAGED // use Lzma SDK for win
+#if defined Q_OS_WIN && !defined TDESKTOP_USE_PACKAGED // use Lzma SDK for win
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
compressed.resize(hSize + resultSize + 1024 * 1024); // rsa signature + sha1 + lzma props + max compressed size
@@ -496,10 +496,8 @@ int main(int argc, char *argv[])
QString outName((targetwin64 ? QString("tx64upd%1") : QString("tupdate%1")).arg(AlphaVersion ? AlphaVersion : version));
#elif defined Q_OS_MAC
QString outName((targetarmac ? QString("tarmacupd%1") : QString("tmacupd%1")).arg(AlphaVersion ? AlphaVersion : version));
-#elif defined Q_OS_UNIX
- QString outName(QString("tlinuxupd%1").arg(AlphaVersion ? AlphaVersion : version));
#else
-#error Unknown platform!
+ QString outName(QString("tlinuxupd%1").arg(AlphaVersion ? AlphaVersion : version));
#endif
if (AlphaVersion) {
outName += "_" + AlphaSignature;
diff --git a/Telegram/SourceFiles/_other/packer.h b/Telegram/SourceFiles/_other/packer.h
index d099ef0e3..4e5fbfc7a 100644
--- a/Telegram/SourceFiles/_other/packer.h
+++ b/Telegram/SourceFiles/_other/packer.h
@@ -27,7 +27,7 @@ extern "C" {
#include
} // extern "C"
-#if defined Q_OS_WIN && !defined DESKTOP_APP_USE_PACKAGED // use Lzma SDK for win
+#if defined Q_OS_WIN && !defined TDESKTOP_USE_PACKAGED // use Lzma SDK for win
#include
#else
#include
diff --git a/Telegram/SourceFiles/_other/updater_linux.cpp b/Telegram/SourceFiles/_other/updater_linux.cpp
index f286b05be..58bc10efa 100644
--- a/Telegram/SourceFiles/_other/updater_linux.cpp
+++ b/Telegram/SourceFiles/_other/updater_linux.cpp
@@ -41,6 +41,7 @@ bool do_mkdir(const char *path) { // from http://stackoverflow.com/questions/675
}
bool _debug = false;
+bool writeprotected = false;
string updaterDir;
string updaterName;
string workDir;
@@ -88,7 +89,7 @@ void writeLog(const char *format, ...) {
va_end(args);
}
-bool copyFile(const char *from, const char *to, bool writeprotected) {
+bool copyFile(const char *from, const char *to) {
FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb");
if (!ffrom) {
if (fto) fclose(fto);
@@ -211,7 +212,7 @@ void delFolder() {
rmdir(delFolder.c_str());
}
-bool update(bool writeprotected) {
+bool update() {
writeLog("Update started..");
string updDir = workDir + "tupdates/temp", readyFilePath = workDir + "tupdates/temp/ready", tdataDir = workDir + "tupdates/temp/tdata";
@@ -324,7 +325,7 @@ bool update(bool writeprotected) {
writeLog("Copying file '%s' to '%s'..", fname.c_str(), tofname.c_str());
int copyTries = 0, triesLimit = 30;
do {
- if (!copyFile(fname.c_str(), tofname.c_str(), writeprotected)) {
+ if (!copyFile(fname.c_str(), tofname.c_str())) {
++copyTries;
usleep(100000);
} else {
@@ -359,10 +360,10 @@ int main(int argc, char *argv[]) {
bool needupdate = true;
bool autostart = false;
bool debug = false;
- bool writeprotected = false;
bool tosettings = false;
bool startintray = false;
bool customWorkingDir = false;
+ bool justUpdate = false;
char *key = 0;
char *workdir = 0;
@@ -381,6 +382,9 @@ int main(int argc, char *argv[]) {
customWorkingDir = true;
} else if (equal(argv[i], "-writeprotected")) {
writeprotected = true;
+ justUpdate = true;
+ } else if (equal(argv[i], "-justupdate")) {
+ justUpdate = true;
} else if (equal(argv[i], "-key") && ++i < argc) {
key = argv[i];
} else if (equal(argv[i], "-workpath") && ++i < argc) {
@@ -455,7 +459,7 @@ int main(int argc, char *argv[]) {
} else {
writeLog("Passed workpath is '%s'", workDir.c_str());
}
- update(writeprotected);
+ update();
}
} else {
writeLog("Error: bad exe name!");
@@ -464,36 +468,38 @@ int main(int argc, char *argv[]) {
writeLog("Error: short exe name!");
}
- const auto fullBinaryPath = exePath + exeName;
-
- auto values = vector();
- const auto push = [&](string arg) {
- // Force null-terminated .data() call result.
- values.push_back(arg + char(0));
- };
- push(!argv0.empty() ? argv0 : fullBinaryPath);
- push("-noupdate");
- if (autostart) push("-autostart");
- if (debug) push("-debug");
- if (startintray) push("-startintray");
- if (tosettings) push("-tosettings");
- if (key) {
- push("-key");
- push(key);
- }
- if (customWorkingDir && workdir) {
- push("-workdir");
- push(workdir);
- }
-
- auto args = vector();
- for (auto &arg : values) {
- args.push_back(arg.data());
- }
- args.push_back(nullptr);
-
// let the parent launch instead
- if (!writeprotected) {
+ if (justUpdate) {
+ writeLog("Closing log and quitting..");
+ } else {
+ const auto fullBinaryPath = exePath + exeName;
+
+ auto values = vector();
+ const auto push = [&](string arg) {
+ // Force null-terminated .data() call result.
+ values.push_back(arg + char(0));
+ };
+ push(!argv0.empty() ? argv0 : fullBinaryPath);
+ push("-noupdate");
+ if (autostart) push("-autostart");
+ if (debug) push("-debug");
+ if (startintray) push("-startintray");
+ if (tosettings) push("-tosettings");
+ if (key) {
+ push("-key");
+ push(key);
+ }
+ if (customWorkingDir && workdir) {
+ push("-workdir");
+ push(workdir);
+ }
+
+ auto args = vector();
+ for (auto &arg : values) {
+ args.push_back(arg.data());
+ }
+ args.push_back(nullptr);
+
pid_t pid = fork();
switch (pid) {
case -1:
@@ -503,9 +509,10 @@ int main(int argc, char *argv[]) {
execv(fullBinaryPath.c_str(), args.data());
return 1;
}
+
+ writeLog("Executed Telegram, closing log and quitting..");
}
- writeLog("Executed Telegram, closing log and quitting..");
closeLog();
return 0;
diff --git a/Telegram/SourceFiles/api/api_blocked_peers.cpp b/Telegram/SourceFiles/api/api_blocked_peers.cpp
index 9dfc886b1..0a79eb733 100644
--- a/Telegram/SourceFiles/api/api_blocked_peers.cpp
+++ b/Telegram/SourceFiles/api/api_blocked_peers.cpp
@@ -77,45 +77,59 @@ void BlockedPeers::block(not_null peer) {
_session->changes().peerUpdated(
peer,
Data::PeerUpdate::Flag::IsBlocked);
- } else if (_blockRequests.find(peer) == end(_blockRequests)) {
- const auto requestId = _api.request(MTPcontacts_Block(
- MTP_flags(0),
- peer->input
- )).done([=] {
- _blockRequests.erase(peer);
- peer->setIsBlocked(true);
- if (_slice) {
- _slice->list.insert(
- _slice->list.begin(),
- { peer->id, base::unixtime::now() });
- ++_slice->total;
- _changes.fire_copy(*_slice);
- }
- }).fail([=] {
- _blockRequests.erase(peer);
- }).send();
-
- _blockRequests.emplace(peer, requestId);
+ return;
+ } else if (blockAlreadySent(peer, true)) {
+ return;
}
+ const auto requestId = _api.request(MTPcontacts_Block(
+ MTP_flags(0),
+ peer->input
+ )).done([=] {
+ const auto data = _blockRequests.take(peer);
+ peer->setIsBlocked(true);
+ if (_slice) {
+ _slice->list.insert(
+ _slice->list.begin(),
+ { peer->id, base::unixtime::now() });
+ ++_slice->total;
+ _changes.fire_copy(*_slice);
+ }
+ if (data) {
+ for (const auto &callback : data->callbacks) {
+ callback(false);
+ }
+ }
+ }).fail([=] {
+ if (const auto data = _blockRequests.take(peer)) {
+ for (const auto &callback : data->callbacks) {
+ callback(false);
+ }
+ }
+ }).send();
+
+ _blockRequests.emplace(peer, Request{
+ .requestId = requestId,
+ .blocking = true,
+ });
}
void BlockedPeers::unblock(
not_null peer,
- Fn onDone,
+ Fn done,
bool force) {
if (!force && !peer->isBlocked()) {
_session->changes().peerUpdated(
peer,
Data::PeerUpdate::Flag::IsBlocked);
return;
- } else if (_blockRequests.find(peer) != end(_blockRequests)) {
+ } else if (blockAlreadySent(peer, false, done)) {
return;
}
const auto requestId = _api.request(MTPcontacts_Unblock(
MTP_flags(0),
peer->input
)).done([=] {
- _blockRequests.erase(peer);
+ const auto data = _blockRequests.take(peer);
peer->setIsBlocked(false);
if (_slice) {
auto &list = _slice->list;
@@ -130,13 +144,46 @@ void BlockedPeers::unblock(
}
_changes.fire_copy(*_slice);
}
- if (onDone) {
- onDone();
+ if (data) {
+ for (const auto &callback : data->callbacks) {
+ callback(true);
+ }
}
}).fail([=] {
- _blockRequests.erase(peer);
+ if (const auto data = _blockRequests.take(peer)) {
+ for (const auto &callback : data->callbacks) {
+ callback(false);
+ }
+ }
}).send();
- _blockRequests.emplace(peer, requestId);
+ const auto i = _blockRequests.emplace(peer, Request{
+ .requestId = requestId,
+ .blocking = false,
+ }).first;
+ if (done) {
+ i->second.callbacks.push_back(std::move(done));
+ }
+}
+
+bool BlockedPeers::blockAlreadySent(
+ not_null peer,
+ bool blocking,
+ Fn done) {
+ const auto i = _blockRequests.find(peer);
+ if (i == end(_blockRequests)) {
+ return false;
+ } else if (i->second.blocking == blocking) {
+ if (done) {
+ i->second.callbacks.push_back(std::move(done));
+ }
+ return true;
+ }
+ const auto callbacks = base::take(i->second.callbacks);
+ _blockRequests.erase(i);
+ for (const auto &callback : callbacks) {
+ callback(false);
+ }
+ return false;
}
void BlockedPeers::reload() {
@@ -160,7 +207,7 @@ auto BlockedPeers::slice() -> rpl::producer {
: (_changes.events() | rpl::type_erased());
}
-void BlockedPeers::request(int offset, Fn onDone) {
+void BlockedPeers::request(int offset, Fn done) {
if (_requestId) {
return;
}
@@ -170,7 +217,7 @@ void BlockedPeers::request(int offset, Fn onDone) {
MTP_int(offset ? kBlockedPerPage : kBlockedFirstSlice)
)).done([=](const MTPcontacts_Blocked &result) {
_requestId = 0;
- onDone(TLToSlice(result, _session->data()));
+ done(TLToSlice(result, _session->data()));
}).fail([=] {
_requestId = 0;
}).send();
diff --git a/Telegram/SourceFiles/api/api_blocked_peers.h b/Telegram/SourceFiles/api/api_blocked_peers.h
index af184c457..ca2000f67 100644
--- a/Telegram/SourceFiles/api/api_blocked_peers.h
+++ b/Telegram/SourceFiles/api/api_blocked_peers.h
@@ -39,20 +39,31 @@ public:
void reload();
rpl::producer slice();
- void request(int offset, Fn onDone);
+ void request(int offset, Fn done);
void block(not_null peer);
void unblock(
not_null peer,
- Fn onDone = nullptr,
+ Fn done = nullptr,
bool force = false);
private:
+ struct Request {
+ std::vector> callbacks;
+ mtpRequestId requestId = 0;
+ bool blocking = false;
+ };
+
+ [[nodiscard]] bool blockAlreadySent(
+ not_null peer,
+ bool blocking,
+ Fn done = nullptr);
+
const not_null _session;
MTP::Sender _api;
- base::flat_map, mtpRequestId> _blockRequests;
+ base::flat_map, Request> _blockRequests;
mtpRequestId _requestId = 0;
std::optional _slice;
rpl::event_stream _changes;
diff --git a/Telegram/SourceFiles/api/api_chat_invite.cpp b/Telegram/SourceFiles/api/api_chat_invite.cpp
index 8b395d9a9..dd06d2abc 100644
--- a/Telegram/SourceFiles/api/api_chat_invite.cpp
+++ b/Telegram/SourceFiles/api/api_chat_invite.cpp
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "window/window_session_controller.h"
+#include "info/profile/info_profile_badge.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/empty_userpic.h"
@@ -25,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h"
#include "boxes/premium_limits_box.h"
#include "styles/style_boxes.h"
+#include "styles/style_info.h"
#include "styles/style_layers.h"
namespace Api {
@@ -195,6 +197,13 @@ ConfirmInviteBox::ConfirmInviteBox(
: _session(session)
, _submit(std::move(submit))
, _title(this, st::confirmInviteTitle)
+, _badge(std::make_unique(
+ this,
+ st::infoPeerBadge,
+ _session,
+ rpl::single(Info::Profile::Badge::Content{ BadgeForInvite(invite) }),
+ nullptr,
+ [=] { return false; }))
, _status(this, st::confirmInviteStatus)
, _about(this, st::confirmInviteAbout)
, _aboutRequests(this, st::confirmInviteStatus)
@@ -275,9 +284,24 @@ ConfirmInviteBox::ChatInvite ConfirmInviteBox::Parse(
.isMegagroup = data.is_megagroup(),
.isBroadcast = data.is_broadcast(),
.isRequestNeeded = data.is_request_needed(),
+ .isFake = data.is_fake(),
+ .isScam = data.is_scam(),
+ .isVerified = data.is_verified(),
};
}
+[[nodiscard]] Info::Profile::BadgeType ConfirmInviteBox::BadgeForInvite(
+ const ChatInvite &invite) {
+ using Type = Info::Profile::BadgeType;
+ return invite.isVerified
+ ? Type::Verified
+ : invite.isScam
+ ? Type::Scam
+ : invite.isFake
+ ? Type::Fake
+ : Type::None;
+}
+
void ConfirmInviteBox::prepare() {
addButton(
(_requestApprove
@@ -326,8 +350,26 @@ void ConfirmInviteBox::prepare() {
void ConfirmInviteBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
- _title->move((width() - _title->width()) / 2, st::confirmInviteTitleTop);
- _status->move((width() - _status->width()) / 2, st::confirmInviteStatusTop);
+
+ const auto padding = st::boxRowPadding;
+ auto nameWidth = width() - padding.left() - padding.right();
+ auto badgeWidth = 0;
+ if (const auto widget = _badge->widget()) {
+ badgeWidth = st::infoVerifiedCheckPosition.x() + widget->width();
+ nameWidth -= badgeWidth;
+ }
+ _title->resizeToWidth(std::min(nameWidth, _title->textMaxWidth()));
+ _title->moveToLeft(
+ (width() - _title->width() - badgeWidth) / 2,
+ st::confirmInviteTitleTop);
+ const auto badgeLeft = _title->x() + _title->width();
+ const auto badgeTop = _title->y();
+ const auto badgeBottom = _title->y() + _title->height();
+ _badge->move(badgeLeft, badgeTop, badgeBottom);
+
+ _status->move(
+ (width() - _status->width()) / 2,
+ st::confirmInviteStatusTop);
auto bottom = _status->y()
+ _status->height()
+ st::boxPadding.bottom()
diff --git a/Telegram/SourceFiles/api/api_chat_invite.h b/Telegram/SourceFiles/api/api_chat_invite.h
index 1ae6f2529..c8d99548c 100644
--- a/Telegram/SourceFiles/api/api_chat_invite.h
+++ b/Telegram/SourceFiles/api/api_chat_invite.h
@@ -12,6 +12,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class UserData;
class ChannelData;
+namespace Info::Profile {
+class Badge;
+enum class BadgeType;
+} // namespace Info::Profile
+
namespace Main {
class Session;
} // namespace Main
@@ -66,10 +71,15 @@ private:
bool isMegagroup = false;
bool isBroadcast = false;
bool isRequestNeeded = false;
+ bool isFake = false;
+ bool isScam = false;
+ bool isVerified = false;
};
[[nodiscard]] static ChatInvite Parse(
not_null session,
const MTPDchatInvite &data);
+ [[nodiscard]] Info::Profile::BadgeType BadgeForInvite(
+ const ChatInvite &invite);
ConfirmInviteBox(
not_null session,
@@ -81,12 +91,14 @@ private:
Fn _submit;
object_ptr _title;
+ std::unique_ptr _badge;
object_ptr _status;
object_ptr _about;
object_ptr _aboutRequests;
std::shared_ptr _photo;
std::unique_ptr _photoEmpty;
std::vector _participants;
+
bool _isChannel = false;
bool _requestApprove = false;
diff --git a/Telegram/SourceFiles/api/api_report.cpp b/Telegram/SourceFiles/api/api_report.cpp
index 501870180..869152737 100644
--- a/Telegram/SourceFiles/api/api_report.cpp
+++ b/Telegram/SourceFiles/api/api_report.cpp
@@ -78,12 +78,8 @@ void SendReport(
MTP_string(comment)
)).done(std::move(done)).send();
}, [&](StoryId id) {
- const auto user = peer->asUser();
- if (!user) {
- return;
- }
peer->session().api().request(MTPstories_Report(
- user->inputUser,
+ peer->input,
MTP_vector(1, MTP_int(id)),
ReasonToTL(reason),
MTP_string(comment)
diff --git a/Telegram/SourceFiles/api/api_websites.cpp b/Telegram/SourceFiles/api/api_websites.cpp
index 855056675..2ea6b2a09 100644
--- a/Telegram/SourceFiles/api/api_websites.cpp
+++ b/Telegram/SourceFiles/api/api_websites.cpp
@@ -17,11 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Api {
namespace {
-constexpr auto TestApiId = 17349;
-constexpr auto SnapApiId = 611335;
-constexpr auto DesktopApiId = 2040;
-
-Websites::Entry ParseEntry(
+[[nodiscard]] Websites::Entry ParseEntry(
not_null owner,
const MTPDwebAuthorization &data) {
auto result = Websites::Entry{
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index 4c551d0e9..8afded01f 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -100,8 +100,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_media_prepare.h"
#include "storage/storage_account.h"
+// AyuGram includes
#include "ayu/ayu_settings.h"
+
namespace {
// Save draft to the cloud with 1 sec extra delay.
@@ -791,7 +793,7 @@ QString ApiWrap::exportDirectStoryLink(not_null story) {
? i->second
: fallback();
request(MTPstories_ExportStoryLink(
- story->peer()->asUser()->inputUser,
+ story->peer()->input,
MTP_int(story->id())
)).done([=](const MTPExportedStoryLink &result) {
const auto link = qs(result.data().vlink());
@@ -2535,14 +2537,9 @@ void ApiWrap::refreshFileReference(
}, [&](Data::FileOriginPremiumPreviews data) {
request(MTPhelp_GetPremiumPromo());
}, [&](Data::FileOriginStory data) {
- const auto user = _session->data().peer(data.peerId)->asUser();
- if (user) {
- request(MTPstories_GetStoriesByID(
- user->inputUser,
- MTP_vector(1, MTP_int(data.storyId))));
- } else {
- fail();
- }
+ request(MTPstories_GetStoriesByID(
+ _session->data().peer(data.peerId)->input,
+ MTP_vector(1, MTP_int(data.storyId))));
}, [&](v::null_t) {
fail();
});
@@ -3322,25 +3319,37 @@ void ApiWrap::shareContact(
const QString &phone,
const QString &firstName,
const QString &lastName,
- const SendAction &action) {
+ const SendAction &action,
+ Fn done) {
const auto userId = UserId(0);
- sendSharedContact(phone, firstName, lastName, userId, action);
+ sendSharedContact(
+ phone,
+ firstName,
+ lastName,
+ userId,
+ action,
+ std::move(done));
}
void ApiWrap::shareContact(
not_null user,
- const SendAction &action) {
+ const SendAction &action,
+ Fn done) {
const auto userId = peerToUser(user->id);
const auto phone = _session->data().findContactPhone(user);
if (phone.isEmpty()) {
+ if (done) {
+ done(false);
+ }
return;
}
- sendSharedContact(
+ return sendSharedContact(
phone,
user->firstName,
user->lastName,
userId,
- action);
+ action,
+ std::move(done));
}
void ApiWrap::sendSharedContact(
@@ -3348,7 +3357,8 @@ void ApiWrap::sendSharedContact(
const QString &firstName,
const QString &lastName,
UserId userId,
- const SendAction &action) {
+ const SendAction &action,
+ Fn done) {
sendAction(action);
const auto history = action.history;
@@ -3399,7 +3409,7 @@ void ApiWrap::sendSharedContact(
MTP_string(firstName),
MTP_string(lastName),
MTP_string()); // vcard
- sendMedia(item, media, action.options);
+ sendMedia(item, media, action.options, std::move(done));
_session->data().sendHistoryChangeNotifications();
_session->changes().historyUpdated(
@@ -3949,18 +3959,20 @@ void ApiWrap::uploadAlbumMedia(
void ApiWrap::sendMedia(
not_null item,
const MTPInputMedia &media,
- Api::SendOptions options) {
+ Api::SendOptions options,
+ Fn done) {
const auto randomId = base::RandomValue();
_session->data().registerMessageRandomId(randomId, item->fullId());
- sendMediaWithRandomId(item, media, options, randomId);
+ sendMediaWithRandomId(item, media, options, randomId, std::move(done));
}
void ApiWrap::sendMediaWithRandomId(
not_null item,
const MTPInputMedia &media,
Api::SendOptions options,
- uint64 randomId) {
+ uint64 randomId,
+ Fn done) {
// AyuGram useScheduledMessages
const auto settings = &AyuSettings::getInstance();
if (settings->useScheduledMessages && !options.scheduled)
@@ -3969,7 +3981,6 @@ void ApiWrap::sendMediaWithRandomId(
auto current = base::unixtime::now();
options.scheduled = current + 12;
}
-
const auto history = item->history();
const auto replyTo = item->replyTo();
@@ -4011,10 +4022,12 @@ void ApiWrap::sendMediaWithRandomId(
MTP_int(options.scheduled),
(options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty())
), [=](const MTPUpdates &result, const MTP::Response &response) {
+ if (done) done(true);
if (updateRecentStickers) {
requestRecentStickersForce(true);
}
}, [=](const MTP::Error &error, const MTP::Response &response) {
+ if (done) done(false);
sendMessageFail(error, peer, randomId, itemId);
});
}
diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h
index 954500f76..f61eb0957 100644
--- a/Telegram/SourceFiles/apiwrap.h
+++ b/Telegram/SourceFiles/apiwrap.h
@@ -294,8 +294,12 @@ public:
const QString &phone,
const QString &firstName,
const QString &lastName,
- const SendAction &action);
- void shareContact(not_null user, const SendAction &action);
+ const SendAction &action,
+ Fn done = nullptr);
+ void shareContact(
+ not_null user,
+ const SendAction &action,
+ Fn done = nullptr);
void applyAffectedMessages(
not_null peer,
const MTPmessages_AffectedMessages &result);
@@ -489,7 +493,8 @@ private:
const QString &firstName,
const QString &lastName,
UserId userId,
- const SendAction &action);
+ const SendAction &action,
+ Fn done);
void deleteHistory(
not_null peer,
@@ -516,12 +521,14 @@ private:
void sendMedia(
not_null item,
const MTPInputMedia &media,
- Api::SendOptions options);
+ Api::SendOptions options,
+ Fn done = nullptr);
void sendMediaWithRandomId(
not_null item,
const MTPInputMedia &media,
Api::SendOptions options,
- uint64 randomId);
+ uint64 randomId,
+ Fn done = nullptr);
FileLoadTo fileLoadTaskOptions(const SendAction &action) const;
void getTopPromotionDelayed(TimeId now, TimeId next);
diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp
index ccea61d70..aab01656d 100644
--- a/Telegram/SourceFiles/boxes/add_contact_box.cpp
+++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp
@@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/toast/toast.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/fields/special_fields.h"
#include "ui/widgets/popup_menu.h"
#include "ui/text/format_values.h"
@@ -297,8 +298,11 @@ void AddContactBox::prepare() {
: tr::lng_enter_contact_data());
updateButtons();
- connect(_first, &Ui::InputField::submitted, [=] { submit(); });
- connect(_last, &Ui::InputField::submitted, [=] { submit(); });
+ const auto submitted = [=] { submit(); };
+ _first->submits(
+ ) | rpl::start_with_next(submitted, _first->lifetime());
+ _last->submits(
+ ) | rpl::start_with_next(submitted, _last->lifetime());
connect(_phone, &Ui::PhoneInput::submitted, [=] { submit(); });
setDimensions(
@@ -567,23 +571,24 @@ void GroupInfoBox::prepare() {
_description->setSubmitSettings(
Core::App().settings().sendSubmitWay());
- connect(_description, &Ui::InputField::resized, [=] {
+ _description->heightChanges(
+ ) | rpl::start_with_next([=] {
descriptionResized();
- });
- connect(_description, &Ui::InputField::submitted, [=] {
- submit();
- });
- connect(_description, &Ui::InputField::cancelled, [=] {
+ }, _description->lifetime());
+ _description->submits(
+ ) | rpl::start_with_next([=] { submit(); }, _description->lifetime());
+ _description->cancelled(
+ ) | rpl::start_with_next([=] {
closeBox();
- });
+ }, _description->lifetime());
Ui::Emoji::SuggestionsController::Init(
getDelegate()->outerContainer(),
_description,
&_navigation->session());
}
-
- connect(_title, &Ui::InputField::submitted, [=] { submitName(); });
+ _title->submits(
+ ) | rpl::start_with_next([=] { submitName(); }, _title->lifetime());
addButton(
((_type != Type::Group || _canAddBot)
@@ -1522,20 +1527,22 @@ void EditNameBox::prepare() {
_first->setMaxLength(Ui::EditPeer::kMaxUserFirstLastName);
_last->setMaxLength(Ui::EditPeer::kMaxUserFirstLastName);
- connect(_first, &Ui::InputField::submitted, [=] { submit(); });
- connect(_last, &Ui::InputField::submitted, [=] { submit(); });
+ _first->submits(
+ ) | rpl::start_with_next([=] { submit(); }, _first->lifetime());
+ _last->submits(
+ ) | rpl::start_with_next([=] { submit(); }, _last->lifetime());
_first->customTab(true);
_last->customTab(true);
- QObject::connect(
- _first,
- &Ui::InputField::tabbed,
- [=] { _last->setFocus(); });
- QObject::connect(
- _last,
- &Ui::InputField::tabbed,
- [=] { _first->setFocus(); });
+ _first->tabbed(
+ ) | rpl::start_with_next([=] {
+ _last->setFocus();
+ }, _first->lifetime());
+ _last->tabbed(
+ ) | rpl::start_with_next([=] {
+ _first->setFocus();
+ }, _last->lifetime());
}
void EditNameBox::setInnerFocus() {
diff --git a/Telegram/SourceFiles/boxes/choose_filter_box.cpp b/Telegram/SourceFiles/boxes/choose_filter_box.cpp
index 12d58b538..bc61e9fe2 100644
--- a/Telegram/SourceFiles/boxes/choose_filter_box.cpp
+++ b/Telegram/SourceFiles/boxes/choose_filter_box.cpp
@@ -32,27 +32,27 @@ Data::ChatFilter ChangedFilter(
auto never = base::duplicate(filter.never());
if (add) {
never.remove(history);
- const auto result = Data::ChatFilter(
- filter.id(),
- filter.title(),
- filter.iconEmoji(),
- filter.flags(),
- filter.always(),
- filter.pinned(),
- std::move(never));
- if (result.contains(history)) {
- return result;
- } else {
- never = base::duplicate(result.never());
- always.insert(history);
- }
} else {
- const auto alwaysIt = always.find(history);
- if (alwaysIt != end(always)) {
- always.erase(alwaysIt);
- } else {
- never.insert(history);
- }
+ always.remove(history);
+ }
+ const auto result = Data::ChatFilter(
+ filter.id(),
+ filter.title(),
+ filter.iconEmoji(),
+ filter.flags(),
+ std::move(always),
+ filter.pinned(),
+ std::move(never));
+ const auto in = result.contains(history);
+ if (in == add) {
+ return result;
+ }
+ always = base::duplicate(result.always());
+ never = base::duplicate(result.never());
+ if (add) {
+ always.insert(history);
+ } else {
+ never.insert(history);
}
return Data::ChatFilter(
filter.id(),
diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp
index 626671cb4..144582a53 100644
--- a/Telegram/SourceFiles/boxes/connection_box.cpp
+++ b/Telegram/SourceFiles/boxes/connection_box.cpp
@@ -18,7 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/facade.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
+#include "ui/widgets/fields/number_input.h"
+#include "ui/widgets/fields/password_input.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/wrap/slide_wrap.h"
diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp
index ca1eb70f3..3274e4eeb 100644
--- a/Telegram/SourceFiles/boxes/create_poll_box.cpp
+++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp
@@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/fade_wrap.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
@@ -184,7 +184,8 @@ not_null CreateWarningLabel(
QString(),
st::createPollWarning);
result->setAttribute(Qt::WA_TransparentForMouseEvents);
- QObject::connect(field, &Ui::InputField::changed, [=] {
+ field->changes(
+ ) | rpl::start_with_next([=] {
Ui::PostponeCall(crl::guard(field, [=] {
const auto length = field->getLastText().size();
const auto value = valueLimit - length;
@@ -198,7 +199,7 @@ not_null CreateWarningLabel(
}
result->setVisible(shown);
}));
- });
+ }, field->lifetime());
return result;
}
@@ -243,13 +244,14 @@ Options::Option::Option(
_content->resize(_content->width(), height);
}, _field->lifetime());
- QObject::connect(_field, &Ui::InputField::changed, [=] {
+ _field->changes(
+ ) | rpl::start_with_next([=] {
Ui::PostponeCall(crl::guard(_field, [=] {
if (_hasCorrect) {
_correct->toggle(isGood(), anim::type::normal);
}
}));
- });
+ }, _field->lifetime());
createShadow();
createRemove();
@@ -303,10 +305,11 @@ void Options::Option::createRemove() {
const auto toggle = lifetime.make_state>(false);
_removeAlways = lifetime.make_state>(false);
- QObject::connect(field, &Ui::InputField::changed, [=] {
+ field->changes(
+ ) | rpl::start_with_next([field, toggle] {
// Don't capture 'this'! Because Option is a value type.
*toggle = !field->getLastText().isEmpty();
- });
+ }, field->lifetime());
rpl::combine(
toggle->value(),
_removeAlways->value(),
@@ -649,28 +652,32 @@ void Options::addEmptyOption() {
_position + _list.size() + _destroyed.size(),
_chooseCorrectGroup));
const auto field = _list.back()->field();
- QObject::connect(field, &Ui::InputField::submitted, [=] {
+ field->submits(
+ ) | rpl::start_with_next([=] {
const auto index = findField(field);
if (_list[index]->isGood() && index + 1 < _list.size()) {
_list[index + 1]->setFocus();
}
- });
- QObject::connect(field, &Ui::InputField::changed, [=] {
+ }, field->lifetime());
+ field->changes(
+ ) | rpl::start_with_next([=] {
Ui::PostponeCall(crl::guard(field, [=] {
validateState();
}));
- });
- QObject::connect(field, &Ui::InputField::focused, [=] {
+ }, field->lifetime());
+ field->focusedChanges(
+ ) | rpl::filter(rpl::mappers::_1) | rpl::start_with_next([=] {
_scrollToWidget.fire_copy(field);
- });
- QObject::connect(field, &Ui::InputField::tabbed, [=] {
+ }, field->lifetime());
+ field->tabbed(
+ ) | rpl::start_with_next([=] {
const auto index = findField(field);
if (index + 1 < _list.size()) {
_list[index + 1]->setFocus();
} else {
_tabbed.fire({});
}
- });
+ }, field->lifetime());
base::install_event_filter(field, [=](not_null event) {
if (event->type() != QEvent::KeyPress
|| !field->getLastText().isEmpty()) {
@@ -927,9 +934,10 @@ object_ptr CreatePollBox::setupContent() {
st::boxDividerLabel),
st::createPollLimitPadding));
- connect(question, &Ui::InputField::tabbed, [=] {
+ question->tabbed(
+ ) | rpl::start_with_next([=] {
options->focusFirst();
- });
+ }, question->lifetime());
AddSkip(container);
AddSubsectionTitle(container, tr::lng_polls_create_settings());
@@ -975,9 +983,10 @@ object_ptr CreatePollBox::setupContent() {
}
}, question->lifetime());
- connect(solution, &Ui::InputField::tabbed, [=] {
+ solution->tabbed(
+ ) | rpl::start_with_next([=] {
question->setFocus();
- });
+ }, solution->lifetime());
quiz->setDisabled(_disabled & PollData::Flag::Quiz);
if (multiple) {
@@ -1009,12 +1018,12 @@ object_ptr CreatePollBox::setupContent() {
const auto text = question->getLastText().trimmed();
return !text.isEmpty() && (text.size() <= kQuestionLimit);
};
-
- connect(question, &Ui::InputField::submitted, [=] {
+ question->submits(
+ ) | rpl::start_with_next([=] {
if (isValidQuestion()) {
options->focusFirst();
}
- });
+ }, question->lifetime());
_setInnerFocus = [=] {
question->setFocusFast();
diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
index 4a2b53c5f..defc4bd64 100644
--- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp
+++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
@@ -54,7 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/painter.h"
#include "ui/ui_utility.h"
#include "ui/widgets/checkbox.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/scroll_area.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
@@ -488,9 +488,16 @@ void EditCaptionBox::setupField() {
Core::App().settings().sendSubmitWay());
_field->setMaxHeight(st::defaultComposeFiles.caption.heightMax);
- connect(_field, &Ui::InputField::submitted, [=] { save(); });
- connect(_field, &Ui::InputField::cancelled, [=] { closeBox(); });
- connect(_field, &Ui::InputField::resized, [=] { captionResized(); });
+ _field->submits(
+ ) | rpl::start_with_next([=] { save(); }, _field->lifetime());
+ _field->cancelled(
+ ) | rpl::start_with_next([=] {
+ closeBox();
+ }, _field->lifetime());
+ _field->heightChanges(
+ ) | rpl::start_with_next([=] {
+ captionResized();
+ }, _field->lifetime());
_field->setMimeDataHook([=](
not_null data,
Ui::InputField::MimeAction action) {
@@ -522,10 +529,11 @@ void EditCaptionBox::setInitialText() {
setCloseByOutsideClick(true);
}
});
- connect(_field, &Ui::InputField::changed, [=] {
+ _field->changes(
+ ) | rpl::start_with_next([=] {
_checkChangedTimer.callOnce(kChangesDebounceTimeout);
setCloseByOutsideClick(false);
- });
+ }, _field->lifetime());
}
void EditCaptionBox::setupControls() {
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
index 88bbdca03..0c3760831 100644
--- a/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_box.cpp
@@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text_utilities.h"
#include "ui/text/text_options.h"
#include "ui/widgets/buttons.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/effects/panel_animation.h"
#include "ui/filter_icons.h"
@@ -619,11 +619,12 @@ void EditFilterBox(
nameEditing->custom = true;
}, box->lifetime());
- QObject::connect(name, &Ui::InputField::changed, [=] {
+ name->changes(
+ ) | rpl::start_with_next([=] {
if (!nameEditing->settingDefault) {
nameEditing->custom = true;
}
- });
+ }, name->lifetime());
const auto updateDefaultTitle = [=](const Data::ChatFilter &filter) {
if (nameEditing->custom) {
return;
diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp
index d7e2c0920..557eefa9a 100644
--- a/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp
+++ b/Telegram/SourceFiles/boxes/filters/edit_filter_links.cpp
@@ -26,7 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/controls/invite_link_label.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/buttons.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/popup_menu.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp
index 5ec269aac..dd165ac61 100644
--- a/Telegram/SourceFiles/boxes/passcode_box.cpp
+++ b/Telegram/SourceFiles/boxes/passcode_box.cpp
@@ -21,10 +21,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/buttons.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
+#include "ui/widgets/fields/password_input.h"
#include "ui/widgets/labels.h"
-#include "ui/widgets/sent_code_field.h"
-#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/painter.h"
#include "passport/passport_encryption.h"
@@ -306,15 +305,26 @@ void PasscodeBox::prepare() {
connect(_oldPasscode, &Ui::MaskedInputField::changed, [=] { oldChanged(); });
connect(_newPasscode, &Ui::MaskedInputField::changed, [=] { newChanged(); });
connect(_reenterPasscode, &Ui::MaskedInputField::changed, [=] { newChanged(); });
- connect(_passwordHint, &Ui::InputField::changed, [=] { newChanged(); });
- connect(_recoverEmail, &Ui::InputField::changed, [=] { emailChanged(); });
+ _passwordHint->changes(
+ ) | rpl::start_with_next([=] {
+ newChanged();
+ }, _passwordHint->lifetime());
+ _recoverEmail->changes(
+ ) | rpl::start_with_next([=] {
+ if (!_emailError.isEmpty()) {
+ _emailError = QString();
+ update();
+ }
+ }, _recoverEmail->lifetime());
const auto fieldSubmit = [=] { submit(); };
connect(_oldPasscode, &Ui::MaskedInputField::submitted, fieldSubmit);
connect(_newPasscode, &Ui::MaskedInputField::submitted, fieldSubmit);
connect(_reenterPasscode, &Ui::MaskedInputField::submitted, fieldSubmit);
- connect(_passwordHint, &Ui::InputField::submitted, fieldSubmit);
- connect(_recoverEmail, &Ui::InputField::submitted, fieldSubmit);
+ _passwordHint->submits(
+ ) | rpl::start_with_next(fieldSubmit, _passwordHint->lifetime());
+ _recoverEmail->submits(
+ ) | rpl::start_with_next(fieldSubmit, _recoverEmail->lifetime());
_recover->addClickHandler([=] { recoverByEmail(); });
@@ -1061,13 +1071,6 @@ void PasscodeBox::newChanged() {
}
}
-void PasscodeBox::emailChanged() {
- if (!_emailError.isEmpty()) {
- _emailError = QString();
- update();
- }
-}
-
void PasscodeBox::recoverByEmail() {
if (!_cloudFields.hasRecovery) {
Assert(_session != nullptr);
@@ -1189,8 +1192,12 @@ void RecoverBox::prepare() {
+ _recoverCode->height()
+ st::passcodeTextLine));
- connect(_recoverCode, &Ui::InputField::changed, [=] { codeChanged(); });
- connect(_recoverCode, &Ui::InputField::submitted, [=] { submit(); });
+ _recoverCode->changes(
+ ) | rpl::start_with_next([=] {
+ codeChanged();
+ }, _recoverCode->lifetime());
+ _recoverCode->submits(
+ ) | rpl::start_with_next([=] { submit(); }, _recoverCode->lifetime());
}
void RecoverBox::paintEvent(QPaintEvent *e) {
diff --git a/Telegram/SourceFiles/boxes/passcode_box.h b/Telegram/SourceFiles/boxes/passcode_box.h
index ee11765a2..75a235b16 100644
--- a/Telegram/SourceFiles/boxes/passcode_box.h
+++ b/Telegram/SourceFiles/boxes/passcode_box.h
@@ -90,7 +90,6 @@ private:
void closeReplacedBy();
void oldChanged();
void newChanged();
- void emailChanged();
void save(bool force = false);
void badOldPasscode();
void recoverByEmail();
diff --git a/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp
index 0aa24c79e..19af19ac6 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp
@@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/checkbox.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/text/format_values.h" // Ui::FormatPhone
#include "ui/text/text_utilities.h"
#include "info/profile/info_profile_cover.h"
@@ -239,8 +239,8 @@ void Controller::initNameFields(
_save();
}
};
- QObject::connect(first, &Ui::InputField::submitted, submit);
- QObject::connect(last, &Ui::InputField::submitted, submit);
+ first->submits() | rpl::start_with_next(submit, first->lifetime());
+ last->submits() | rpl::start_with_next(submit, last->lifetime());
first->setMaxLength(Ui::EditPeer::kMaxUserFirstLastName);
first->setMaxLength(Ui::EditPeer::kMaxUserFirstLastName);
}
diff --git a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp
index d5dbc6789..5caa2ce5c 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/peers/edit_forum_topic_box.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/shadow.h"
#include "ui/effects/emoji_fly_animation.h"
#include "ui/abstract_button.h"
@@ -465,15 +465,13 @@ void EditForumTopicBox(
ChooseNextColorId(current.colorId, state->otherColorIds),
};
});
- base::qt_signal_producer(
- title,
- &Ui::InputField::changed
+ title->changes(
) | rpl::start_with_next([=] {
state->defaultIcon = DefaultIcon{
title->getLastText().trimmed(),
state->defaultIcon.current().colorId,
};
- }, box->lifetime());
+ }, title->lifetime());
if (!topic || !topic->isGeneral()) {
Settings::AddDividerText(
diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
index f47beae75..d867c0607 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
@@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/box_content_divider.h"
#include "ui/layers/generic_box.h"
#include "ui/toast/toast.h"
@@ -384,8 +384,11 @@ void EditAdminBox::prepare() {
if (!_saveCallback) {
return;
} else if (_addAsAdmin && !_addAsAdmin->checked()) {
+ const auto weak = Ui::MakeWeak(this);
AddBotToGroup(user(), peer(), _addingBot->token);
- getDelegate()->hideLayer();
+ if (const auto strong = weak.data()) {
+ strong->closeBox();
+ }
return;
} else if (_addingBot && !_addingBot->existing) {
const auto phrase = peer()->isBroadcast()
@@ -461,13 +464,14 @@ not_null EditAdminBox::addRankInput(
st::rightsAboutMargin);
result->setMaxLength(kAdminRoleLimit);
result->setInstantReplaces(Ui::InstantReplaces::TextOnly());
- connect(result, &Ui::InputField::changed, [=] {
+ result->changes(
+ ) | rpl::start_with_next([=] {
const auto text = result->getLastText();
const auto removed = TextUtilities::RemoveEmoji(text);
if (removed != text) {
result->setText(removed);
}
- });
+ }, result->lifetime());
container->add(
object_ptr(
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
index 01251e1f8..3e95dcc1f 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
@@ -51,7 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text_utilities.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/box_content_divider.h"
#include "ui/wrap/padding_wrap.h"
@@ -519,10 +519,10 @@ object_ptr Controller::createTitleEdit() {
result->entity(),
&_peer->session());
- QObject::connect(
- result->entity(),
- &Ui::InputField::submitted,
- [=] { submitTitle(); });
+ result->entity()->submits(
+ ) | rpl::start_with_next([=] {
+ submitTitle();
+ }, result->entity()->lifetime());
_controls.title = result->entity();
return result;
@@ -555,10 +555,10 @@ object_ptr Controller::createDescriptionEdit() {
result->entity(),
&_peer->session());
- QObject::connect(
- result->entity(),
- &Ui::InputField::submitted,
- [=] { submitDescription(); });
+ result->entity()->submits(
+ ) | rpl::start_with_next([=] {
+ submitDescription();
+ }, result->entity()->lifetime());
_controls.description = result->entity();
return result;
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp
index 97a86be3b..755368192 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp
@@ -51,13 +51,14 @@ constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000);
}
[[nodiscard]] auto NestedRestrictionLabelsList(
- Data::RestrictionsSetOptions options) {
+ Data::RestrictionsSetOptions options)
+-> std::vector> {
using Flag = ChatRestriction;
auto first = std::vector{
{ Flag::SendOther, tr::lng_rights_chat_send_text(tr::now) },
};
- auto inner = std::vector{
+ auto media = std::vector{
{ Flag::SendPhotos, tr::lng_rights_chat_photos(tr::now) },
{ Flag::SendVideos, tr::lng_rights_chat_videos(tr::now) },
{ Flag::SendVideoMessages, tr::lng_rights_chat_video_messages(tr::now) },
@@ -85,9 +86,64 @@ constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000);
&RestrictionLabel::flags),
end(second));
}
- return std::vector>{
+ return {
{ std::nullopt, std::move(first) },
- { tr::lng_rights_chat_send_media(), std::move(inner) },
+ { tr::lng_rights_chat_send_media(), std::move(media) },
+ { std::nullopt, std::move(second) },
+ };
+}
+
+[[nodiscard]] auto NestedAdminRightLabels(
+ Data::AdminRightsSetOptions options)
+-> std::vector> {
+ using Flag = ChatAdminRight;
+
+ if (options.isGroup) {
+ auto result = std::vector{
+ { Flag::ChangeInfo, tr::lng_rights_group_info(tr::now) },
+ { Flag::DeleteMessages, tr::lng_rights_group_delete(tr::now) },
+ { Flag::BanUsers, tr::lng_rights_group_ban(tr::now) },
+ { Flag::InviteByLinkOrAdd, options.anyoneCanAddMembers
+ ? tr::lng_rights_group_invite_link(tr::now)
+ : tr::lng_rights_group_invite(tr::now) },
+ { Flag::ManageTopics, tr::lng_rights_group_topics(tr::now) },
+ { Flag::PinMessages, tr::lng_rights_group_pin(tr::now) },
+ { Flag::ManageCall, tr::lng_rights_group_manage_calls(tr::now) },
+ { Flag::Anonymous, tr::lng_rights_group_anonymous(tr::now) },
+ { Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) },
+ };
+ if (!options.isForum) {
+ result.erase(
+ ranges::remove(
+ result,
+ Flag::ManageTopics | Flag(),
+ &AdminRightLabel::flags),
+ end(result));
+ }
+ return { { std::nullopt, std::move(result) } };
+ }
+ auto first = std::vector{
+ { Flag::ChangeInfo, tr::lng_rights_channel_info(tr::now) },
+ };
+ auto messages = std::vector{
+ { Flag::PostMessages, tr::lng_rights_channel_post(tr::now) },
+ { Flag::EditMessages, tr::lng_rights_channel_edit(tr::now) },
+ { Flag::DeleteMessages, tr::lng_rights_channel_delete(tr::now) },
+ };
+ auto stories = std::vector{
+ { Flag::PostStories, tr::lng_rights_channel_post_stories(tr::now) },
+ { Flag::EditStories, tr::lng_rights_channel_edit_stories(tr::now) },
+ { Flag::DeleteStories, tr::lng_rights_channel_delete_stories(tr::now) },
+ };
+ auto second = std::vector{
+ { Flag::InviteByLinkOrAdd, tr::lng_rights_group_invite(tr::now) },
+ { Flag::ManageCall, tr::lng_rights_channel_manage_calls(tr::now) },
+ { Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) },
+ };
+ return {
+ { std::nullopt, std::move(first) },
+ { tr::lng_rights_channel_manage(), std::move(messages) },
+ { tr::lng_rights_channel_manage_stories(), std::move(stories) },
{ std::nullopt, std::move(second) },
};
}
@@ -1031,42 +1087,11 @@ std::vector RestrictionLabels(
std::vector AdminRightLabels(
Data::AdminRightsSetOptions options) {
- using Flag = ChatAdminRight;
-
- if (options.isGroup) {
- auto result = std::vector{
- { Flag::ChangeInfo, tr::lng_rights_group_info(tr::now) },
- { Flag::DeleteMessages, tr::lng_rights_group_delete(tr::now) },
- { Flag::BanUsers, tr::lng_rights_group_ban(tr::now) },
- { Flag::InviteByLinkOrAdd, options.anyoneCanAddMembers
- ? tr::lng_rights_group_invite_link(tr::now)
- : tr::lng_rights_group_invite(tr::now) },
- { Flag::ManageTopics, tr::lng_rights_group_topics(tr::now) },
- { Flag::PinMessages, tr::lng_rights_group_pin(tr::now) },
- { Flag::ManageCall, tr::lng_rights_group_manage_calls(tr::now) },
- { Flag::Anonymous, tr::lng_rights_group_anonymous(tr::now) },
- { Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) },
- };
- if (!options.isForum) {
- result.erase(
- ranges::remove(
- result,
- Flag::ManageTopics | Flag(),
- &AdminRightLabel::flags),
- end(result));
- }
- return result;
- } else {
- return {
- { Flag::ChangeInfo, tr::lng_rights_channel_info(tr::now) },
- { Flag::PostMessages, tr::lng_rights_channel_post(tr::now) },
- { Flag::EditMessages, tr::lng_rights_channel_edit(tr::now) },
- { Flag::DeleteMessages, tr::lng_rights_channel_delete(tr::now) },
- { Flag::InviteByLinkOrAdd, tr::lng_rights_group_invite(tr::now) },
- { Flag::ManageCall, tr::lng_rights_channel_manage_calls(tr::now) },
- { Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) }
- };
+ auto result = std::vector();
+ for (const auto &[_, r] : NestedAdminRightLabels(options)) {
+ result.insert(result.end(), r.begin(), r.end());
}
+ return result;
}
EditFlagsControl CreateEditRestrictions(
@@ -1107,7 +1132,7 @@ EditFlagsControl CreateEditAdminRights(
rights,
{
.header = std::move(header),
- .labels = { { std::nullopt, AdminRightLabels(options) } },
+ .labels = NestedAdminRightLabels(options),
.disabledMessages = std::move(disabledMessages),
});
result.widget = std::move(widget);
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp
index 2739eb125..bae0be7fe 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp
@@ -32,7 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/controls/userpic_button.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/box_content_divider.h"
#include "ui/wrap/padding_wrap.h"
diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.cpp b/Telegram/SourceFiles/boxes/premium_limits_box.cpp
index 8980a9028..928915afd 100644
--- a/Telegram/SourceFiles/boxes/premium_limits_box.cpp
+++ b/Telegram/SourceFiles/boxes/premium_limits_box.cpp
@@ -48,19 +48,6 @@ struct InfographicDescriptor {
bool complexRatio = false;
};
-[[nodiscard]] rpl::producer<> BoxShowFinishes(not_null box) {
- const auto singleShot = box->lifetime().make_state();
- const auto showFinishes = singleShot->make_state>();
-
- box->setShowFinishedCallback([=] {
- showFinishes->fire({});
- singleShot->destroy();
- box->setShowFinishedCallback(nullptr);
- });
-
- return showFinishes->events();
-}
-
void AddSubsectionTitle(
not_null container,
rpl::producer text) {
@@ -423,8 +410,9 @@ void SimpleLimitBox(
Settings::AddSkip(top, st::premiumInfographicPadding.top());
Ui::Premium::AddBubbleRow(
top,
+ st::defaultPremiumBubble,
BoxShowFinishes(box),
- descriptor.defaultLimit,
+ 0,
descriptor.current,
descriptor.premiumLimit,
premiumPossible,
@@ -783,16 +771,18 @@ void FilterLinksLimitBox(
void FiltersLimitBox(
not_null box,
- not_null session) {
+ not_null session,
+ std::optional filtersCountOverride) {
const auto premium = session->premium();
const auto premiumPossible = session->premiumPossible();
const auto limits = Data::PremiumLimits(session);
const auto defaultLimit = float64(limits.dialogFiltersDefault());
const auto premiumLimit = float64(limits.dialogFiltersPremium());
- const auto current = float64(ranges::count_if(
+ const auto cloud = int(ranges::count_if(
session->data().chatsFilters().list(),
[](const Data::ChatFilter &f) { return f.id() != FilterId(); }));
+ const auto current = float64(filtersCountOverride.value_or(cloud));
auto text = rpl::combine(
tr::lng_filters_limit1(
@@ -1092,6 +1082,7 @@ void AccountsLimitBox(
Settings::AddSkip(top, st::premiumInfographicPadding.top());
Ui::Premium::AddBubbleRow(
top,
+ st::defaultPremiumBubble,
BoxShowFinishes(box),
0,
current,
diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.h b/Telegram/SourceFiles/boxes/premium_limits_box.h
index 7bcda6e53..5a4fa8a0a 100644
--- a/Telegram/SourceFiles/boxes/premium_limits_box.h
+++ b/Telegram/SourceFiles/boxes/premium_limits_box.h
@@ -42,7 +42,8 @@ void FilterLinksLimitBox(
not_null session);
void FiltersLimitBox(
not_null box,
- not_null session);
+ not_null session,
+ std::optional filtersCountOverride);
void ShareableFiltersLimitBox(
not_null box,
not_null session);
diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp
index 50f8886d9..025b4082c 100644
--- a/Telegram/SourceFiles/boxes/send_files_box.cpp
+++ b/Telegram/SourceFiles/boxes/send_files_box.cpp
@@ -31,7 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/scroll_content_shadow.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/popup_menu.h"
#include "ui/wrap/vertical_layout.h"
@@ -1033,17 +1033,21 @@ void SendFilesBox::setupCaption() {
Core::App().settings().sendSubmitWay());
_caption->setMaxLength(kMaxMessageLength);
- connect(_caption, &Ui::InputField::resized, [=] {
+ _caption->heightChanges(
+ ) | rpl::start_with_next([=] {
captionResized();
- });
- connect(_caption, &Ui::InputField::submitted, [=](
- Qt::KeyboardModifiers modifiers) {
+ }, _caption->lifetime());
+ _caption->submits(
+ ) | rpl::start_with_next([=](Qt::KeyboardModifiers modifiers) {
const auto ctrlShiftEnter = modifiers.testFlag(Qt::ShiftModifier)
&& (modifiers.testFlag(Qt::ControlModifier)
|| modifiers.testFlag(Qt::MetaModifier));
send({}, ctrlShiftEnter);
- });
- connect(_caption, &Ui::InputField::cancelled, [=] { closeBox(); });
+ }, _caption->lifetime());
+ _caption->cancelled(
+ ) | rpl::start_with_next([=] {
+ closeBox();
+ }, _caption->lifetime());
_caption->setMimeDataHook([=](
not_null data,
Ui::InputField::MimeAction action) {
diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp
index e30174be6..d458111f2 100644
--- a/Telegram/SourceFiles/boxes/sessions_box.cpp
+++ b/Telegram/SourceFiles/boxes/sessions_box.cpp
@@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/widgets/buttons.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/scroll_area.h"
#include "ui/wrap/slide_wrap.h"
@@ -144,7 +144,7 @@ void RenameBox(not_null box) {
Core::App().settings().setCustomDeviceModel(result);
Core::App().saveSettingsDelayed();
};
- QObject::connect(name, &Ui::InputField::submitted, submit);
+ name->submits() | rpl::start_with_next(submit, name->lifetime());
box->addButton(tr::lng_settings_save(), submit);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp
index 154961016..ad992b525 100644
--- a/Telegram/SourceFiles/boxes/share_box.cpp
+++ b/Telegram/SourceFiles/boxes/share_box.cpp
@@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/checkbox.h"
#include "ui/widgets/multi_select.h"
#include "ui/widgets/scroll_area.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/popup_menu.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/text/text_options.h"
@@ -226,9 +226,8 @@ void ShareBox::prepareCommentField() {
const auto field = _comment->entity();
- connect(field, &Ui::InputField::submitted, [=] {
- submit({});
- });
+ field->submits(
+ ) | rpl::start_with_next([=] { submit({}); }, field->lifetime());
if (const auto show = uiShow(); show->valid()) {
InitMessageFieldHandlers(
_descriptor.session,
@@ -249,6 +248,8 @@ void ShareBox::prepareCommentField() {
void ShareBox::prepare() {
prepareCommentField();
+ setCloseByOutsideClick(false);
+
_select->resizeToWidth(st::boxWideWidth);
Ui::SendPendingMoveResizeEvents(_select);
diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp
index b1eef8d91..4f8c682a0 100644
--- a/Telegram/SourceFiles/boxes/stickers_box.cpp
+++ b/Telegram/SourceFiles/boxes/stickers_box.cpp
@@ -31,7 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/ripple_animation.h"
#include "ui/effects/slide_animation.h"
#include "ui/widgets/discrete_sliders.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/image/image.h"
#include "ui/cached_round_corners.h"
#include "ui/painter.h"
diff --git a/Telegram/SourceFiles/calls/group/calls_group_menu.cpp b/Telegram/SourceFiles/calls/group/calls_group_menu.cpp
index aad072371..51a9068a7 100644
--- a/Telegram/SourceFiles/calls/group/calls_group_menu.cpp
+++ b/Telegram/SourceFiles/calls/group/calls_group_menu.cpp
@@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/menu/menu_action.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/checkbox.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/effects/ripple_animation.h"
#include "ui/layers/generic_box.h"
#include "ui/painter.h"
diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp
index 210eb0dce..6002963a9 100644
--- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp
+++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp
@@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/call_button.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/dropdown_menu.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/tooltip.h"
#include "ui/widgets/rp_window.h"
#include "ui/chat/group_call_bar.h"
diff --git a/Telegram/SourceFiles/calls/group/calls_group_settings.cpp b/Telegram/SourceFiles/calls/group/calls_group_settings.cpp
index 59ed26a62..121d6d037 100644
--- a/Telegram/SourceFiles/calls/group/calls_group_settings.cpp
+++ b/Telegram/SourceFiles/calls/group/calls_group_settings.cpp
@@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/continuous_sliders.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/popup_menu.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/text/text_utilities.h"
diff --git a/Telegram/SourceFiles/calls/group/ui/calls_group_recording_box.cpp b/Telegram/SourceFiles/calls/group/ui/calls_group_recording_box.cpp
index 5d084bb37..30a492a3f 100644
--- a/Telegram/SourceFiles/calls/group/ui/calls_group_recording_box.cpp
+++ b/Telegram/SourceFiles/calls/group/ui/calls_group_recording_box.cpp
@@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image_prepare.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/checkbox.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/labels.h"
#include "styles/style_calls.h"
#include "styles/style_layers.h"
@@ -287,7 +287,7 @@ void EditGroupCallTitleBox(
box->closeBox();
done(result);
};
- QObject::connect(input, &Ui::InputField::submitted, submit);
+ input->submits() | rpl::start_with_next(submit, input->lifetime());
box->addButton(tr::lng_settings_save(), submit);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
@@ -346,7 +346,7 @@ void AddTitleGroupCallRecordingBox(
box->closeBox();
done(result);
};
- QObject::connect(input, &Ui::InputField::submitted, submit);
+ input->submits() | rpl::start_with_next(submit, input->lifetime());
box->addButton(tr::lng_group_call_recording_start_button(), submit);
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
diff --git a/Telegram/SourceFiles/chat_helpers/emoji_sets_manager.cpp b/Telegram/SourceFiles/chat_helpers/emoji_sets_manager.cpp
index cc70419d5..7c6ae96c8 100644
--- a/Telegram/SourceFiles/chat_helpers/emoji_sets_manager.cpp
+++ b/Telegram/SourceFiles/chat_helpers/emoji_sets_manager.cpp
@@ -41,9 +41,9 @@ inline auto PreviewPath(int i) {
const auto kSets = {
Set{ { 0, 0, 0, "Mac" }, PreviewPath(0) },
- Set{ { 1, 1392, 8'184'590, "Android" }, PreviewPath(1) },
- Set{ { 2, 1393, 5'413'219, "Twemoji" }, PreviewPath(2) },
- Set{ { 3, 1394, 6'967'218, "JoyPixels" }, PreviewPath(3) },
+ Set{ { 1, 1804, 8'115'639, "Android" }, PreviewPath(1) },
+ Set{ { 2, 1805, 5'481'197, "Twemoji" }, PreviewPath(2) },
+ Set{ { 3, 1806, 7'047'594, "JoyPixels" }, PreviewPath(3) },
};
using Loading = MTP::DedicatedLoader::Progress;
diff --git a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp
index a97d1fe42..f6c54ec6d 100644
--- a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp
+++ b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp
@@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/ripple_animation.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/inner_dropdown.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/emoji_config.h"
#include "ui/ui_utility.h"
#include "ui/cached_round_corners.h"
diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
index 8f5f5fec5..2c7bf803c 100644
--- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
+++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp
@@ -31,7 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/clip/media_clip_reader.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/scroll_area.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/text/text_options.h"
#include "ui/image/image.h"
#include "ui/effects/path_shift_gradient.h"
diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp
index a5765a031..a334d8d81 100644
--- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp
+++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp
@@ -24,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/click_handler_types.h"
#include "ui/controls/tabbed_search.h"
#include "ui/widgets/buttons.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/popup_menu.h"
#include "ui/effects/ripple_animation.h"
#include "ui/image/image.h"
diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp
index 304c7d3a0..a9c0fd789 100644
--- a/Telegram/SourceFiles/chat_helpers/message_field.cpp
+++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp
@@ -190,16 +190,18 @@ void EditLinkBox(
}
};
- QObject::connect(text, &Ui::InputField::submitted, [=] {
+ text->submits(
+ ) | rpl::start_with_next([=] {
url->setFocusFast();
- });
- QObject::connect(url, &Ui::InputField::submitted, [=] {
+ }, text->lifetime());
+ url->submits(
+ ) | rpl::start_with_next([=] {
if (text->getLastText().isEmpty()) {
text->setFocusFast();
} else {
submit();
}
- });
+ }, url->lifetime());
box->setTitle(url->getLastText().isEmpty()
? tr::lng_formatting_link_create_title()
@@ -223,8 +225,14 @@ void EditLinkBox(
url->customTab(true);
text->customTab(true);
- QObject::connect(url, &Ui::InputField::tabbed, [=] { text->setFocus(); });
- QObject::connect(text, &Ui::InputField::tabbed, [=] { url->setFocus(); });
+ url->tabbed(
+ ) | rpl::start_with_next([=] {
+ text->setFocus();
+ }, url->lifetime());
+ text->tabbed(
+ ) | rpl::start_with_next([=] {
+ url->setFocus();
+ }, text->lifetime());
}
TextWithEntities StripSupportHashtag(TextWithEntities text) {
@@ -590,7 +598,8 @@ AutocompleteQuery ParseMentionHashtagBotCommandQuery(
MessageLinksParser::MessageLinksParser(not_null field)
: _field(field)
, _timer([=] { parse(); }) {
- _connection = QObject::connect(_field, &Ui::InputField::changed, [=] {
+ _lifetime = _field->changes(
+ ) | rpl::start_with_next([=] {
const auto length = _field->getTextWithTags().text.size();
const auto timeout = (std::abs(length - _lastLength) > 2)
? 0
diff --git a/Telegram/SourceFiles/chat_helpers/message_field.h b/Telegram/SourceFiles/chat_helpers/message_field.h
index acc2808a1..9c4ba031f 100644
--- a/Telegram/SourceFiles/chat_helpers/message_field.h
+++ b/Telegram/SourceFiles/chat_helpers/message_field.h
@@ -7,9 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "base/timer.h"
-#include "base/qt_connection.h"
#include "chat_helpers/compose/compose_features.h"
#ifndef TDESKTOP_DISABLE_SPELLCHECK
@@ -132,7 +131,7 @@ private:
int _lastLength = 0;
bool _disabled = false;
base::Timer _timer;
- base::qt_connection _connection;
+ rpl::lifetime _lifetime;
};
diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp
index 15e2e8c34..0c09b6dd3 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp
+++ b/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp
@@ -26,7 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lottie/lottie_single_player.h"
#include "ui/dpr/dpr_icon.h"
#include "ui/dpr/dpr_image.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/buttons.h"
#include "ui/painter.h"
#include "ui/rect_part.h"
diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp
index 957b83f18..09810abfd 100644
--- a/Telegram/SourceFiles/core/changelogs.cpp
+++ b/Telegram/SourceFiles/core/changelogs.cpp
@@ -22,110 +22,6 @@ namespace {
std::map BetaLogs() {
return {
- {
- 4005004,
- "- Allow wide range of interface scale options.\n"
-
- "- Show opened chat name in the window title.\n"
-
- "- Bug fixes and other minor improvements.\n"
-
- "- Fix updating on macOS older than 10.14.\n"
- },
- {
- 4005006,
- "- Try enabling non-fractional scale "
- "High DPI support on Windows and Linux.\n"
-
- "- Experimental setting for fractional scale "
- "High DPI support on Windows and Linux.\n"
-
- "- Fix navigation to bottom problems in groups you didn't join.\n"
-
- "- Fix a crash in chat export settings changes.\n"
-
- "- Fix a crash in sending some of JPEG images.\n"
-
- "- Fix CJK fonts on Windows.\n"
- },
- {
- 4005007,
- "- Fix glitches after moving window to another screen.\n",
- },
- {
- 4005008,
- "- Allow opening another account in a new window "
- "(see Settings > Advanced > Experimental Settings).\n"
-
- "- A lot of bugfixes for working with more than one window.\n"
- },
- {
- 4006004,
- "- Allow media viewer to exit fullscreen and become a normal window."
- },
- {
- 4006006,
- "- Confirmation window before starting a call.\n"
-
- "- New \"Battery and Animations\" settings section.\n"
-
- "- \"Save Power on Low Battery\" option for laptops.\n"
-
- "- Improved windowed mode support for media viewer.\n"
-
- "- Hardware accelerated video playback fix on macOS.\n"
-
- "- New application icon on macOS following the system guidelines.\n"
- },
- {
- 4006007,
- "- Fix crash when accepting incoming calls.\n"
-
- "- Remove sound when cancelling an unconfirmed call.\n"
- },
- {
- 4006008,
- "- Improve quality of voice messages with changed playback speed.\n"
-
- "- Show when your message was read in small groups.\n"
-
- "- Fix pasting images from Firefox on Windows.\n"
-
- "- Improve memory usage for custom emoji.\n"
- },
- {
- 4006010,
- "- Suggest sending an invite link if user forbids "
- "inviting him to groups.\n"
-
- "- Show when a reaction was left on your message in small groups.\n"
-
- "- Fix a crash in video chats on Windows.\n"
-
- "- Fix a crash in audio speed change.\n"
- },
- {
- 4006011,
- "- Allow larger interface scale values on high-dpi screens.\n"
-
- "- Implement new voice and video speed change interface (up to 2.5x).\n"
-
- "- Support global Fn+F shortcut to toggle fullscreen on macOS.\n"
-
- "- Silent notification sound in Focus Mode on macOS.\n"
-
- "- Fix media viewer on macOS with several screens.\n"
-
- "- Fix a crash in connection type box.\n"
-
- "- Fix possible crash on quit.\n"
- },
- {
- 4006012,
- "- Fix several possible crashes.\n"
-
- "- Deprecate macOS 10.12, Ubuntu 18.04 and CentOS 7 in July.\n"
- },
{
4008011,
"- Fix initial video playback speed.\n"
diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp
index c21a206e9..abefbd3b4 100644
--- a/Telegram/SourceFiles/core/core_settings.cpp
+++ b/Telegram/SourceFiles/core/core_settings.cpp
@@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/send_files_box.h"
#include "history/view/history_view_quick_action.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "storage/serialize_common.h"
#include "window/section_widget.h"
#include "base/platform/base_platform_info.h"
diff --git a/Telegram/SourceFiles/core/crash_reports.cpp b/Telegram/SourceFiles/core/crash_reports.cpp
index 24e22982c..4c1a4f50e 100644
--- a/Telegram/SourceFiles/core/crash_reports.cpp
+++ b/Telegram/SourceFiles/core/crash_reports.cpp
@@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include
#include
-#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
+#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
#ifdef Q_OS_WIN
#include
@@ -25,7 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include
#pragma warning(pop)
-#elif defined Q_OS_UNIX // Q_OS_WIN
+#else // Q_OS_WIN
#include
#include
@@ -48,7 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#endif // Q_OS_MAC
#endif // Q_OS_WIN
-#endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
+#endif // !TDESKTOP_DISABLE_CRASH_REPORTS
namespace CrashReports {
namespace {
@@ -59,7 +59,7 @@ using AnnotationRefs = std::map;
Annotations ProcessAnnotations;
AnnotationRefs ProcessAnnotationRefs;
-#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
+#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
QString ReportPath;
FILE *ReportFile = nullptr;
@@ -197,13 +197,15 @@ const int HandledSignals[] = {
SIGABRT,
SIGFPE,
SIGILL,
-#ifdef Q_OS_UNIX
+#ifndef Q_OS_WIN
SIGBUS,
SIGTRAP,
-#endif // Q_OS_UNIX
+#endif // !Q_OS_WIN
};
-#ifdef Q_OS_UNIX
+#ifdef Q_OS_WIN
+void SignalHandler(int signum) {
+#else // Q_OS_WIN
struct sigaction OldSigActions[32]/* = { 0 }*/;
void RestoreSignalHandlers() {
@@ -229,9 +231,7 @@ void InvokeOldSignalHandler(int signum, siginfo_t *info, void *ucontext) {
void SignalHandler(int signum, siginfo_t *info, void *ucontext) {
RestoreSignalHandlers();
-#else // Q_OS_UNIX
-void SignalHandler(int signum) {
-#endif // else for Q_OS_UNIX
+#endif // else for Q_OS_WIN
const char* name = 0;
switch (signum) {
@@ -253,9 +253,9 @@ void SignalHandler(int signum) {
ReportingThreadId = nullptr;
}
-#ifdef Q_OS_UNIX
+#ifndef Q_OS_WIN
InvokeOldSignalHandler(signum, info, ucontext);
-#endif // Q_OS_UNIX
+#endif // !Q_OS_WIN
}
bool SetSignalHandlers = true;
@@ -267,9 +267,9 @@ google_breakpad::ExceptionHandler* BreakpadExceptionHandler = 0;
bool DumpCallback(const wchar_t* _dump_dir, const wchar_t* _minidump_id, void* context, EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion, bool success)
#elif defined Q_OS_MAC // Q_OS_WIN
bool DumpCallback(const char* _dump_dir, const char* _minidump_id, void *context, bool success)
-#elif defined Q_OS_UNIX // Q_OS_MAC
+#else // Q_OS_MAC
bool DumpCallback(const google_breakpad::MinidumpDescriptor &md, void *context, bool success)
-#endif // Q_OS_UNIX
+#endif // else for Q_OS_WIN || Q_OS_MAC
{
if (CrashLogged) return success;
CrashLogged = true;
@@ -290,7 +290,7 @@ bool DumpCallback(const google_breakpad::MinidumpDescriptor &md, void *context,
}
#endif // !Q_OS_MAC || MAC_USE_BREAKPAD
-#endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
+#endif // !TDESKTOP_DISABLE_CRASH_REPORTS
} // namespace
@@ -314,7 +314,7 @@ QString PlatformString() {
}
void StartCatching() {
-#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
+#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
ProcessAnnotations["Binary"] = cExeName().toUtf8().constData();
ProcessAnnotations["ApiId"] = QString::number(ApiId).toUtf8().constData();
ProcessAnnotations["Version"] = (cAlphaVersion()
@@ -370,7 +370,7 @@ void StartCatching() {
false)) { // asynchronous_start
}
#endif // else for MAC_USE_BREAKPAD
-#elif defined Q_OS_UNIX
+#else
BreakpadExceptionHandler = new google_breakpad::ExceptionHandler(
google_breakpad::MinidumpDescriptor(QFile::encodeName(dumpspath).toStdString()),
/*FilterCallback*/ 0,
@@ -379,22 +379,22 @@ void StartCatching() {
true,
-1
);
-#endif // Q_OS_UNIX
-#endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
+#endif // else for Q_OS_WIN || Q_OS_MAC
+#endif // !TDESKTOP_DISABLE_CRASH_REPORTS
}
void FinishCatching() {
-#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
+#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
#if !defined Q_OS_MAC || defined MAC_USE_BREAKPAD
delete base::take(BreakpadExceptionHandler);
#endif // !Q_OS_MAC || MAC_USE_BREAKPAD
-#endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
+#endif // !TDESKTOP_DISABLE_CRASH_REPORTS
}
StartResult Start() {
-#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
+#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
ReportPath = cWorkingDir() + u"tdata/working"_q;
#ifdef Q_OS_WIN
@@ -420,12 +420,12 @@ StartResult Start() {
return lastdump;
}
-#endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
+#endif // !TDESKTOP_DISABLE_CRASH_REPORTS
return Restart();
}
Status Restart() {
-#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
+#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
if (ReportFile) {
return Started;
}
@@ -470,13 +470,13 @@ Status Restart() {
LOG(("FATAL: Could not open '%1' for writing!").arg(ReportPath));
return CantOpen;
-#else // !DESKTOP_APP_DISABLE_CRASH_REPORTS
+#else // !TDESKTOP_DISABLE_CRASH_REPORTS
return Started;
-#endif // else for !DESKTOP_APP_DISABLE_CRASH_REPORTS
+#endif // else for !TDESKTOP_DISABLE_CRASH_REPORTS
}
void Finish() {
-#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
+#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
FinishCatching();
if (ReportFile) {
@@ -489,7 +489,7 @@ void Finish() {
unlink(ReportPath.toUtf8().constData());
#endif // else for Q_OS_WIN
}
-#endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
+#endif // !TDESKTOP_DISABLE_CRASH_REPORTS
}
void SetAnnotation(const std::string &key, const QString &value) {
@@ -539,7 +539,7 @@ void SetAnnotationRef(const std::string &key, const QString *valuePtr) {
}
}
-#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
+#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
dump::~dump() {
if (ReportFile) {
@@ -604,6 +604,6 @@ const dump &operator<<(const dump &stream, double num) {
return stream;
}
-#endif // DESKTOP_APP_DISABLE_CRASH_REPORTS
+#endif // TDESKTOP_DISABLE_CRASH_REPORTS
} // namespace CrashReports
diff --git a/Telegram/SourceFiles/core/crash_reports.h b/Telegram/SourceFiles/core/crash_reports.h
index 6a1f0e1d4..bfe09e240 100644
--- a/Telegram/SourceFiles/core/crash_reports.h
+++ b/Telegram/SourceFiles/core/crash_reports.h
@@ -11,7 +11,7 @@ namespace CrashReports {
QString PlatformString();
-#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
+#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
struct dump {
~dump();
@@ -24,7 +24,7 @@ const dump &operator<<(const dump &stream, unsigned long num);
const dump &operator<<(const dump &stream, unsigned long long num);
const dump &operator<<(const dump &stream, double num);
-#endif // DESKTOP_APP_DISABLE_CRASH_REPORTS
+#endif // TDESKTOP_DISABLE_CRASH_REPORTS
enum Status {
CantOpen,
diff --git a/Telegram/SourceFiles/core/deadlock_detector.h b/Telegram/SourceFiles/core/deadlock_detector.h
new file mode 100644
index 000000000..fb538cf62
--- /dev/null
+++ b/Telegram/SourceFiles/core/deadlock_detector.h
@@ -0,0 +1,83 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+namespace Core::DeadlockDetector {
+
+class PingPongEvent : public QEvent {
+public:
+ static auto Type() {
+ static const auto Result = QEvent::Type(QEvent::registerEventType());
+ return Result;
+ }
+
+ PingPongEvent(not_null sender)
+ : QEvent(Type())
+ , _sender(sender) {
+ }
+
+ [[nodiscard]] not_null sender() const {
+ return _sender;
+ }
+
+private:
+ not_null _sender;
+
+};
+
+class Pinger : public QObject {
+public:
+ Pinger(not_null receiver)
+ : _receiver(receiver)
+ , _abortTimer([] { Unexpected("Deadlock found!"); }) {
+ const auto callback = [=] {
+ QCoreApplication::postEvent(_receiver, new PingPongEvent(this));
+ _abortTimer.callOnce(30000);
+ };
+ _pingTimer.setCallback(callback);
+ _pingTimer.callEach(60000);
+ callback();
+ }
+
+protected:
+ bool event(QEvent *e) override {
+ if (e->type() == PingPongEvent::Type()
+ && static_cast(e)->sender() == _receiver) {
+ _abortTimer.cancel();
+ }
+ return QObject::event(e);
+ }
+
+private:
+ not_null _receiver;
+ base::Timer _pingTimer;
+ base::Timer _abortTimer;
+
+};
+
+class PingThread : public QThread {
+public:
+ PingThread(not_null parent)
+ : QThread(parent) {
+ start();
+ }
+
+ ~PingThread() {
+ quit();
+ wait();
+ }
+
+protected:
+ void run() override {
+ Pinger pinger(parent());
+ QThread::run();
+ }
+
+};
+
+} // namespace Core::DeadlockDetector
diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp
index fee3b0140..fa0a728fa 100644
--- a/Telegram/SourceFiles/core/launcher.cpp
+++ b/Telegram/SourceFiles/core/launcher.cpp
@@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/options.h"
#include
+#include
namespace Core {
namespace {
@@ -115,16 +116,26 @@ void ComputeDebugMode() {
}
void ComputeExternalUpdater() {
- QFile file(u"/etc/tdesktop/externalupdater"_q);
-
- if (file.exists() && file.open(QIODevice::ReadOnly)) {
- QTextStream fileStream(&file);
- while (!fileStream.atEnd()) {
- const auto path = fileStream.readLine();
-
- if (path == (cExeDir() + cExeName())) {
- SetUpdaterDisabledAtStartup();
- return;
+ auto locations = QStandardPaths::standardLocations(
+ QStandardPaths::AppDataLocation);
+ if (locations.isEmpty()) {
+ locations << QString();
+ }
+ locations[0] = QDir::cleanPath(cWorkingDir());
+ locations << QDir::cleanPath(cExeDir());
+ for (const auto &location : locations) {
+ const auto dir = location + u"/externalupdater.d"_q;
+ for (const auto &info : QDir(dir).entryInfoList(QDir::Files)) {
+ QFile file(info.absoluteFilePath());
+ if (file.open(QIODevice::ReadOnly)) {
+ QTextStream fileStream(&file);
+ while (!fileStream.atEnd()) {
+ const auto path = fileStream.readLine();
+ if (path == (cExeDir() + cExeName())) {
+ SetUpdaterDisabledAtStartup();
+ return;
+ }
+ }
}
}
}
@@ -223,7 +234,7 @@ bool CheckPortableVersionFolder() {
if (cAlphaVersion()) {
Assert(*AlphaPrivateKey != 0);
- cForceWorkingDir(portable + '/');
+ cForceWorkingDir(portable);
QDir().mkpath(cWorkingDir() + u"tdata"_q);
cSetAlphaPrivateKey(QByteArray(AlphaPrivateKey));
if (!key.open(QIODevice::WriteOnly)) {
@@ -239,7 +250,7 @@ bool CheckPortableVersionFolder() {
if (!QDir(portable).exists()) {
return true;
}
- cForceWorkingDir(portable + '/');
+ cForceWorkingDir(portable);
if (!key.exists()) {
return true;
}
@@ -291,7 +302,8 @@ Launcher::Launcher(int argc, char *argv[])
: _argc(argc)
, _argv(argv)
, _arguments(readArguments(_argc, _argv))
-, _baseIntegration(_argc, _argv) {
+, _baseIntegration(_argc, _argv)
+, _initialWorkingDir(QDir::currentPath() + '/') {
crl::toggle_fp_exceptions(true);
base::Integration::Set(&_baseIntegration);
@@ -446,6 +458,10 @@ const QStringList &Launcher::arguments() const {
return _arguments;
}
+QString Launcher::initialWorkingDir() const {
+ return _initialWorkingDir;
+}
+
bool Launcher::customWorkingDir() const {
return !_customWorkingDir.isEmpty();
}
diff --git a/Telegram/SourceFiles/core/launcher.h b/Telegram/SourceFiles/core/launcher.h
index a5e5117a5..ea5b1c97f 100644
--- a/Telegram/SourceFiles/core/launcher.h
+++ b/Telegram/SourceFiles/core/launcher.h
@@ -29,6 +29,7 @@ public:
virtual int exec();
const QStringList &arguments() const;
+ QString initialWorkingDir() const;
bool customWorkingDir() const;
uint64 installationTag() const;
@@ -84,6 +85,7 @@ private:
QStringList _arguments;
BaseIntegration _baseIntegration;
+ QString _initialWorkingDir;
QString _customWorkingDir;
};
diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp
index 9daf74d62..d9db3c540 100644
--- a/Telegram/SourceFiles/core/local_url_handlers.cpp
+++ b/Telegram/SourceFiles/core/local_url_handlers.cpp
@@ -378,6 +378,8 @@ bool ResolveUsernameOrPhone(
startToken = params.value(u"startgroup"_q);
} else if (params.contains(u"startchannel"_q)) {
resolveType = ResolveType::AddToChannel;
+ } else if (params.contains(u"boost"_q)) {
+ resolveType = ResolveType::Boost;
}
auto post = ShowAtUnreadMsgId;
auto adminRights = ChatAdminRights();
@@ -392,7 +394,6 @@ bool ResolveUsernameOrPhone(
const auto storyParam = params.value(u"story"_q);
const auto storyId = storyParam.toInt();
const auto appname = webChannelPreviewLink ? QString() : appnameParam;
- const auto appstart = params.value(u"startapp"_q);
const auto commentParam = params.value(u"comment"_q);
const auto commentId = commentParam.toInt();
const auto topicParam = params.value(u"topic"_q);
@@ -404,11 +405,11 @@ bool ResolveUsernameOrPhone(
startToken = gameParam;
resolveType = ResolveType::ShareGame;
}
- if (startToken.isEmpty() && params.contains(u"startapp"_q)) {
- startToken = params.value(u"startapp"_q);
- }
if (!appname.isEmpty()) {
resolveType = ResolveType::BotApp;
+ if (startToken.isEmpty() && params.contains(u"startapp"_q)) {
+ startToken = params.value(u"startapp"_q);
+ }
}
const auto myContext = context.value();
using Navigation = Window::SessionNavigation;
@@ -436,7 +437,11 @@ bool ResolveUsernameOrPhone(
.attachBotUsername = params.value(u"attach"_q),
.attachBotToggleCommand = (params.contains(u"startattach"_q)
? params.value(u"startattach"_q)
+ : (appname.isEmpty() && params.contains(u"startapp"_q))
+ ? params.value(u"startapp"_q)
: std::optional()),
+ .attachBotMenuOpen = (appname.isEmpty()
+ && params.contains(u"startapp"_q)),
.attachBotChooseTypes = InlineBots::ParseChooseTypes(
params.value(u"choose"_q)),
.voicechatHash = (params.contains(u"livestream"_q)
@@ -839,6 +844,32 @@ bool ResolveLoginCode(
return true;
}
+bool ResolveBoost(
+ Window::SessionController *controller,
+ const Match &match,
+ const QVariant &context) {
+ if (!controller) {
+ return false;
+ }
+ const auto params = url_parse_params(
+ match->captured(1),
+ qthelp::UrlParamNameTransform::ToLower);
+ const auto domainParam = params.value(u"domain"_q);
+ const auto channelParam = params.value(u"channel"_q);
+
+ const auto myContext = context.value();
+ using Navigation = Window::SessionNavigation;
+ controller->window().activate();
+ controller->showPeerByLink(Navigation::PeerByLinkInfo{
+ .usernameOrId = (!domainParam.isEmpty()
+ ? std::variant(domainParam)
+ : ChannelId(BareId(channelParam.toULongLong()))),
+ .resolveType = Window::ResolveType::Boost,
+ .clickFromMessageId = myContext.itemId,
+ });
+ return true;
+}
+
} // namespace
const std::vector &LocalUrlHandlers() {
@@ -919,6 +950,10 @@ const std::vector &LocalUrlHandlers() {
u"^login/?(\\?code=([0-9]+))(&|$)"_q,
ResolveLoginCode
},
+ {
+ u"^boost/?\\?(.+)(#|$)"_q,
+ ResolveBoost,
+ },
{
u"^([^\\?]+)(\\?|#|$)"_q,
HandleUnknown
@@ -1022,8 +1057,13 @@ QString TryConvertUrlToLocal(QString url) {
"/\\d+/?(\\?|$)|"
"/\\d+/\\d+/?(\\?|$)"
")"_q, query, matchOptions)) {
+ const auto channel = privateMatch->captured(1);
const auto params = query.mid(privateMatch->captured(0).size()).toString();
- const auto base = u"tg://privatepost?channel="_q + privateMatch->captured(1);
+ if (params.indexOf("boost", 0, Qt::CaseInsensitive) >= 0
+ && params.toLower().split('&').contains(u"boost"_q)) {
+ return u"tg://boost?channel="_q + channel;
+ }
+ const auto base = u"tg://privatepost?channel="_q + channel;
auto added = QString();
if (const auto threadPostMatch = regex_match(u"^/(\\d+)/(\\d+)(/?\\?|/?$)"_q, privateMatch->captured(2))) {
added = u"&topic=%1&post=%2"_q.arg(threadPostMatch->captured(1)).arg(threadPostMatch->captured(2));
@@ -1041,7 +1081,12 @@ QString TryConvertUrlToLocal(QString url) {
"/s/\\d+/?(\\?|$)|"
"/\\d+/\\d+/?(\\?|$)"
")"_q, query, matchOptions)) {
+ const auto domain = usernameMatch->captured(1);
const auto params = query.mid(usernameMatch->captured(0).size()).toString();
+ if (params.indexOf("boost", 0, Qt::CaseInsensitive) >= 0
+ && params.toLower().split('&').contains(u"boost"_q)) {
+ return u"tg://boost?domain="_q + domain;
+ }
const auto base = u"tg://resolve?domain="_q + url_encode(usernameMatch->captured(1));
auto added = QString();
if (const auto threadPostMatch = regex_match(u"^/(\\d+)/(\\d+)(/?\\?|/?$)"_q, usernameMatch->captured(2))) {
diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp
index 1c1419bb2..2c42064ca 100644
--- a/Telegram/SourceFiles/core/sandbox.cpp
+++ b/Telegram/SourceFiles/core/sandbox.cpp
@@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/launcher.h"
#include "core/local_url_handlers.h"
#include "core/update_checker.h"
+#include "core/deadlock_detector.h"
#include "base/timer.h"
#include "base/concurrent_timer.h"
#include "base/invoke_queued.h"
@@ -202,6 +203,13 @@ void Sandbox::launchApplication() {
}
setupScreenScale();
+#ifndef _DEBUG
+ if (Logs::DebugEnabled()) {
+ using DeadlockDetector::PingThread;
+ _deadlockDetector = std::make_unique(this);
+ }
+#endif // !_DEBUG
+
_application = std::make_unique();
// Ideally this should go to constructor.
@@ -271,6 +279,10 @@ bool Sandbox::event(QEvent *e) {
return false;
} else if (e->type() == QEvent::Close) {
Quit();
+ } else if (e->type() == DeadlockDetector::PingPongEvent::Type()) {
+ postEvent(
+ static_cast(e)->sender(),
+ new DeadlockDetector::PingPongEvent(this));
}
return QApplication::event(e);
}
diff --git a/Telegram/SourceFiles/core/sandbox.h b/Telegram/SourceFiles/core/sandbox.h
index d8f47cd5a..4c15c2828 100644
--- a/Telegram/SourceFiles/core/sandbox.h
+++ b/Telegram/SourceFiles/core/sandbox.h
@@ -131,6 +131,8 @@ private:
rpl::event_stream<> _widgetUpdateRequests;
+ std::unique_ptr _deadlockDetector;
+
};
} // namespace Core
diff --git a/Telegram/SourceFiles/core/ui_integration.cpp b/Telegram/SourceFiles/core/ui_integration.cpp
index ead942262..8f1be8324 100644
--- a/Telegram/SourceFiles/core/ui_integration.cpp
+++ b/Telegram/SourceFiles/core/ui_integration.cpp
@@ -370,7 +370,6 @@ QString UiIntegration::phrasePanelCloseAnyway() {
return tr::lng_bot_close_warning_sure(tr::now);
}
-#if 0 // disabled for now
QString UiIntegration::phraseBotSharePhone() {
return tr::lng_bot_share_phone(tr::now);
}
@@ -382,7 +381,18 @@ QString UiIntegration::phraseBotSharePhoneTitle() {
QString UiIntegration::phraseBotSharePhoneConfirm() {
return tr::lng_bot_share_phone_confirm(tr::now);
}
-#endif
+
+QString UiIntegration::phraseBotAllowWrite() {
+ return tr::lng_bot_allow_write(tr::now);
+}
+
+QString UiIntegration::phraseBotAllowWriteTitle() {
+ return tr::lng_bot_allow_write_title(tr::now);
+}
+
+QString UiIntegration::phraseBotAllowWriteConfirm() {
+ return tr::lng_bot_allow_write_confirm(tr::now);
+}
bool OpenGLLastCheckFailed() {
return QFile::exists(OpenGLCheckFilePath());
diff --git a/Telegram/SourceFiles/core/ui_integration.h b/Telegram/SourceFiles/core/ui_integration.h
index 65661ecfd..cd3e4ff58 100644
--- a/Telegram/SourceFiles/core/ui_integration.h
+++ b/Telegram/SourceFiles/core/ui_integration.h
@@ -84,11 +84,12 @@ public:
QString phrasePanelCloseWarning() override;
QString phrasePanelCloseUnsaved() override;
QString phrasePanelCloseAnyway() override;
-#if 0 // disabled for now
QString phraseBotSharePhone() override;
QString phraseBotSharePhoneTitle() override;
QString phraseBotSharePhoneConfirm() override;
-#endif
+ QString phraseBotAllowWrite() override;
+ QString phraseBotAllowWriteTitle() override;
+ QString phraseBotAllowWriteConfirm() override;
};
diff --git a/Telegram/SourceFiles/core/update_checker.cpp b/Telegram/SourceFiles/core/update_checker.cpp
index 6c7154f84..0ff7b4cc1 100644
--- a/Telegram/SourceFiles/core/update_checker.cpp
+++ b/Telegram/SourceFiles/core/update_checker.cpp
@@ -42,16 +42,16 @@ extern "C" {
} // extern "C"
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
-#if defined Q_OS_WIN && !defined DESKTOP_APP_USE_PACKAGED // use Lzma SDK for win
+#if defined Q_OS_WIN && !defined TDESKTOP_USE_PACKAGED // use Lzma SDK for win
#include
-#else // Q_OS_WIN && !DESKTOP_APP_USE_PACKAGED
+#else // Q_OS_WIN && !TDESKTOP_USE_PACKAGED
#include
-#endif // else of Q_OS_WIN && !DESKTOP_APP_USE_PACKAGED
+#endif // else of Q_OS_WIN && !TDESKTOP_USE_PACKAGED
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
-#ifdef Q_OS_UNIX
+#ifndef Q_OS_WIN
#include
-#endif // Q_OS_UNIX
+#endif // !Q_OS_WIN
namespace Core {
namespace {
@@ -275,11 +275,11 @@ bool UnpackUpdate(const QString &filepath) {
return false;
}
-#if defined Q_OS_WIN && !defined DESKTOP_APP_USE_PACKAGED // use Lzma SDK for win
+#if defined Q_OS_WIN && !defined TDESKTOP_USE_PACKAGED // use Lzma SDK for win
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
-#else // Q_OS_WIN && !DESKTOP_APP_USE_PACKAGED
+#else // Q_OS_WIN && !TDESKTOP_USE_PACKAGED
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = 0, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hOriginalSizeLen; // header
-#endif // Q_OS_WIN && !DESKTOP_APP_USE_PACKAGED
+#endif // Q_OS_WIN && !TDESKTOP_USE_PACKAGED
QByteArray compressed = input.readAll();
int32 compressedLen = compressed.size() - hSize;
@@ -330,14 +330,14 @@ bool UnpackUpdate(const QString &filepath) {
uncompressed.resize(uncompressedLen);
size_t resultLen = uncompressed.size();
-#if defined Q_OS_WIN && !defined DESKTOP_APP_USE_PACKAGED // use Lzma SDK for win
+#if defined Q_OS_WIN && !defined TDESKTOP_USE_PACKAGED // use Lzma SDK for win
SizeT srcLen = compressedLen;
int uncompressRes = LzmaUncompress((uchar*)uncompressed.data(), &resultLen, (const uchar*)(compressed.constData() + hSize), &srcLen, (const uchar*)(compressed.constData() + hSigLen + hShaLen), LZMA_PROPS_SIZE);
if (uncompressRes != SZ_OK) {
LOG(("Update Error: could not uncompress lzma, code: %1").arg(uncompressRes));
return false;
}
-#else // Q_OS_WIN && !DESKTOP_APP_USE_PACKAGED
+#else // Q_OS_WIN && !TDESKTOP_USE_PACKAGED
lzma_stream stream = LZMA_STREAM_INIT;
lzma_ret ret = lzma_stream_decoder(&stream, UINT64_MAX, LZMA_CONCATENATED);
@@ -380,7 +380,7 @@ bool UnpackUpdate(const QString &filepath) {
LOG(("Error in decompression: %1 (error code %2)").arg(msg).arg(res));
return false;
}
-#endif // Q_OS_WIN && !DESKTOP_APP_USE_PACKAGED
+#endif // Q_OS_WIN && !TDESKTOP_USE_PACKAGED
tempDir.mkdir(tempDir.absolutePath());
@@ -428,9 +428,9 @@ bool UnpackUpdate(const QString &filepath) {
bool executable = false;
stream >> relativeName >> fileSize >> fileInnerData;
-#ifdef Q_OS_UNIX
+#ifndef Q_OS_WIN
stream >> executable;
-#endif // Q_OS_UNIX
+#endif // !Q_OS_WIN
if (stream.status() != QDataStream::Ok) {
LOG(("Update Error: cant read file from downloaded stream, status: %1").arg(stream.status()));
return false;
@@ -1539,10 +1539,10 @@ bool checkReadyUpdate() {
#elif defined Q_OS_MAC // Q_OS_WIN
QString curUpdater = (cExeDir() + cExeName() + u"/Contents/Frameworks/Updater"_q);
QFileInfo updater(cWorkingDir() + u"tupdates/temp/Telegram.app/Contents/Frameworks/Updater"_q);
-#elif defined Q_OS_UNIX // Q_OS_MAC
+#else // Q_OS_MAC
QString curUpdater = (cExeDir() + u"Updater"_q);
QFileInfo updater(cWorkingDir() + u"tupdates/temp/Updater"_q);
-#endif // Q_OS_UNIX
+#endif // else for Q_OS_WIN || Q_OS_MAC
if (!updater.exists()) {
QFileInfo current(curUpdater);
if (!current.exists()) {
@@ -1576,7 +1576,7 @@ bool checkReadyUpdate() {
ClearAll();
return false;
}
-#elif defined Q_OS_UNIX // Q_OS_MAC
+#else // Q_OS_MAC
// if the files in the directory are owned by user, while the directory is not,
// update will still fail since it's not possible to remove files
if (QFile::exists(curUpdater)
@@ -1604,7 +1604,7 @@ bool checkReadyUpdate() {
return false;
}
}
-#endif // Q_OS_UNIX
+#endif // else for Q_OS_WIN || Q_OS_MAC
#ifdef Q_OS_MAC
base::Platform::RemoveQuarantine(QFileInfo(curUpdater).absolutePath());
diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h
index fec69bc64..bc435194b 100644
--- a/Telegram/SourceFiles/core/version.h
+++ b/Telegram/SourceFiles/core/version.h
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
constexpr auto AppName = "AyuGram Desktop"_cs;
constexpr auto AppFile = "AyuGram"_cs;
-constexpr auto AppVersion = 4009004;
-constexpr auto AppVersionStr = "4.9.4";
+constexpr auto AppVersion = 4010000;
+constexpr auto AppVersionStr = "4.10";
constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
diff --git a/Telegram/SourceFiles/data/data_bot_app.h b/Telegram/SourceFiles/data/data_bot_app.h
index 7de4aefe8..715fb03c0 100644
--- a/Telegram/SourceFiles/data/data_bot_app.h
+++ b/Telegram/SourceFiles/data/data_bot_app.h
@@ -24,4 +24,5 @@ struct BotAppData {
uint64 accessHash = 0;
uint64 hash = 0;
+ bool hasSettings = false;
};
diff --git a/Telegram/SourceFiles/data/data_changes.h b/Telegram/SourceFiles/data/data_changes.h
index 304749900..12cfa7353 100644
--- a/Telegram/SourceFiles/data/data_changes.h
+++ b/Telegram/SourceFiles/data/data_changes.h
@@ -221,14 +221,14 @@ struct StoryUpdate {
enum class Flag : uint32 {
None = 0,
- Edited = (1U << 0),
- Destroyed = (1U << 1),
- NewAdded = (1U << 2),
- ViewsAdded = (1U << 3),
- MarkRead = (1U << 4),
- Reaction = (1U << 5),
+ Edited = (1U << 0),
+ Destroyed = (1U << 1),
+ NewAdded = (1U << 2),
+ ViewsChanged = (1U << 3),
+ MarkRead = (1U << 4),
+ Reaction = (1U << 5),
- LastUsedBit = (1U << 5),
+ LastUsedBit = (1U << 5),
};
using Flags = base::flags;
friend inline constexpr auto is_flag_type(Flag) { return true; }
diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp
index dddc44116..cf266c5a2 100644
--- a/Telegram/SourceFiles/data/data_channel.cpp
+++ b/Telegram/SourceFiles/data/data_channel.cpp
@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "data/data_chat.h"
#include "data/data_session.h"
+#include "data/data_stories.h"
#include "data/data_folder.h"
#include "data/data_forum.h"
#include "data/data_forum_icons.h"
@@ -530,6 +531,11 @@ bool ChannelData::canBanMembers() const {
|| (adminRights() & AdminRight::BanUsers);
}
+bool ChannelData::canPostMessages() const {
+ return amCreator()
+ || (adminRights() & AdminRight::PostMessages);
+}
+
bool ChannelData::canEditMessages() const {
return amCreator()
|| (adminRights() & AdminRight::EditMessages);
@@ -540,6 +546,30 @@ bool ChannelData::canDeleteMessages() const {
|| (adminRights() & AdminRight::DeleteMessages);
}
+bool ChannelData::canPostStories() const {
+ if (!isBroadcast()) {
+ return false;
+ }
+ return amCreator()
+ || (adminRights() & AdminRight::PostStories);
+}
+
+bool ChannelData::canEditStories() const {
+ if (!isBroadcast()) {
+ return false;
+ }
+ return amCreator()
+ || (adminRights() & AdminRight::EditStories);
+}
+
+bool ChannelData::canDeleteStories() const {
+ if (!isBroadcast()) {
+ return false;
+ }
+ return amCreator()
+ || (adminRights() & AdminRight::DeleteStories);
+}
+
bool ChannelData::anyoneCanAddMembers() const {
return !(defaultRestrictions() & Restriction::AddParticipants);
}
@@ -559,11 +589,6 @@ bool ChannelData::canAddAdmins() const {
|| (adminRights() & AdminRight::AddAdmins);
}
-bool ChannelData::canPublish() const {
- return amCreator()
- || (adminRights() & AdminRight::PostMessages);
-}
-
bool ChannelData::allowsForwarding() const {
return !(flags() & Flag::NoForwards);
}
@@ -877,6 +902,38 @@ const Data::AllowedReactions &ChannelData::allowedReactions() const {
return _allowedReactions;
}
+bool ChannelData::hasActiveStories() const {
+ return flags() & Flag::HasActiveStories;
+}
+
+bool ChannelData::hasUnreadStories() const {
+ return flags() & Flag::HasUnreadStories;
+}
+
+void ChannelData::setStoriesState(StoriesState state) {
+ Expects(state != StoriesState::Unknown);
+
+ const auto was = flags();
+ switch (state) {
+ case StoriesState::None:
+ _flags.remove(Flag::HasActiveStories | Flag::HasUnreadStories);
+ break;
+ case StoriesState::HasRead:
+ _flags.set(
+ (flags() & ~Flag::HasUnreadStories) | Flag::HasActiveStories);
+ break;
+ case StoriesState::HasUnread:
+ _flags.add(Flag::HasActiveStories | Flag::HasUnreadStories);
+ break;
+ }
+ if (flags() != was) {
+ if (const auto history = owner().historyLoaded(this)) {
+ history->updateChatListEntryPostponed();
+ }
+ session().changes().peerUpdated(this, UpdateFlag::StoriesState);
+ }
+}
+
void ChannelData::processTopics(const MTPVector &topics) {
if (const auto forum = this->forum()) {
forum->applyReceivedTopics(topics);
@@ -1046,6 +1103,7 @@ void ApplyChannelUpdate(
} else {
channel->setAllowedReactions({});
}
+ channel->owner().stories().apply(channel, update.vstories());
channel->fullUpdated();
channel->setPendingRequestsCount(
update.vrequests_pending().value_or_empty(),
diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h
index 6c05d5c88..ee0550a6d 100644
--- a/Telegram/SourceFiles/data/data_channel.h
+++ b/Telegram/SourceFiles/data/data_channel.h
@@ -59,6 +59,9 @@ enum class ChannelDataFlag {
Forum = (1 << 23),
AntiSpam = (1 << 24),
ParticipantsHidden = (1 << 25),
+ StoriesHidden = (1 << 26),
+ HasActiveStories = (1 << 27),
+ HasUnreadStories = (1 << 28),
};
inline constexpr bool is_flag_type(ChannelDataFlag) { return true; };
using ChannelDataFlags = base::flags;
@@ -226,6 +229,9 @@ public:
[[nodiscard]] bool isFake() const {
return flags() & Flag::Fake;
}
+ [[nodiscard]] bool hasStoriesHidden() const {
+ return flags() & Flag::StoriesHidden;
+ }
[[nodiscard]] static ChatRestrictionsInfo KickedRestrictedRights(
not_null participant);
@@ -329,10 +335,13 @@ public:
[[nodiscard]] bool canBanMembers() const;
[[nodiscard]] bool anyoneCanAddMembers() const;
+ [[nodiscard]] bool canPostMessages() const;
[[nodiscard]] bool canEditMessages() const;
[[nodiscard]] bool canDeleteMessages() const;
+ [[nodiscard]] bool canPostStories() const;
+ [[nodiscard]] bool canEditStories() const;
+ [[nodiscard]] bool canDeleteStories() const;
[[nodiscard]] bool hiddenPreHistory() const;
- [[nodiscard]] bool canPublish() const;
[[nodiscard]] bool canViewMembers() const;
[[nodiscard]] bool canViewAdmins() const;
[[nodiscard]] bool canViewBanned() const;
@@ -437,6 +446,10 @@ public:
void setAllowedReactions(Data::AllowedReactions value);
[[nodiscard]] const Data::AllowedReactions &allowedReactions() const;
+ [[nodiscard]] bool hasActiveStories() const;
+ [[nodiscard]] bool hasUnreadStories() const;
+ void setStoriesState(StoriesState state);
+
[[nodiscard]] Data::Forum *forum() const {
return mgInfo ? mgInfo->forum() : nullptr;
}
diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.cpp b/Telegram/SourceFiles/data/data_chat_participant_status.cpp
index 836f13ebc..f875ccdb3 100644
--- a/Telegram/SourceFiles/data/data_chat_participant_status.cpp
+++ b/Telegram/SourceFiles/data/data_chat_participant_status.cpp
@@ -146,7 +146,7 @@ bool CanSendAnyOf(
&& !(channel->flags() & Flag::JoinToWrite));
if (!allowed || (forbidInForums && channel->isForum())) {
return false;
- } else if (channel->canPublish()) {
+ } else if (channel->canPostMessages()) {
return true;
} else if (channel->isBroadcast()) {
return false;
diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.h b/Telegram/SourceFiles/data/data_chat_participant_status.h
index 2227e8042..8dd7cd682 100644
--- a/Telegram/SourceFiles/data/data_chat_participant_status.h
+++ b/Telegram/SourceFiles/data/data_chat_participant_status.h
@@ -25,6 +25,9 @@ enum class ChatAdminRight {
ManageCall = (1 << 11),
Other = (1 << 12),
ManageTopics = (1 << 13),
+ PostStories = (1 << 14),
+ EditStories = (1 << 15),
+ DeleteStories = (1 << 16),
};
inline constexpr bool is_flag_type(ChatAdminRight) { return true; }
using ChatAdminRights = base::flags;
diff --git a/Telegram/SourceFiles/data/data_document_resolver.cpp b/Telegram/SourceFiles/data/data_document_resolver.cpp
index 2839eeb76..ead282bcf 100644
--- a/Telegram/SourceFiles/data/data_document_resolver.cpp
+++ b/Telegram/SourceFiles/data/data_document_resolver.cpp
@@ -159,14 +159,7 @@ wv xm xml ym yuv").split(' ');
bool IsExecutableName(const QString &filepath) {
static const auto kExtensions = [] {
const auto joined =
-#ifdef Q_OS_MAC
- u"\
-applescript action app bin command csh osx workflow terminal url caction \
-mpkg pkg scpt scptd xhtm webarchive"_q;
-#elif defined Q_OS_UNIX // Q_OS_MAC
- u"bin csh deb desktop ksh out pet pkg pup rpm run sh shar \
-slp zsh"_q;
-#else // Q_OS_MAC || Q_OS_UNIX
+#ifdef Q_OS_WIN
u"\
ad ade adp app application appref-ms asp asx bas bat bin cab cdxml cer cfg \
chi chm cmd cnt com cpl crt csh der diagcab dll drv eml exe fon fxp gadget \
@@ -179,7 +172,14 @@ psd1 psm1 pssc pst py py3 pyc pyd pyi pyo pyw pywz pyz rb reg rgs scf scr \
sct search-ms settingcontent-ms sh shb shs slk sys t tmp u3p url vb vbe vbp \
vbs vbscript vdx vsmacros vsd vsdm vsdx vss vssm vssx vst vstm vstx vsw vsx \
vtx website ws wsc wsf wsh xbap xll xnk xs"_q;
-#endif // !Q_OS_MAC && !Q_OS_UNIX
+#elif defined Q_OS_MAC // Q_OS_MAC
+ u"\
+applescript action app bin command csh osx workflow terminal url caction \
+mpkg pkg scpt scptd xhtm webarchive"_q;
+#else // Q_OS_WIN || Q_OS_MAC
+ u"bin csh deb desktop ksh out pet pkg pup rpm run sh shar \
+slp zsh"_q;
+#endif // !Q_OS_WIN && !Q_OS_MAC
const auto list = joined.split(' ');
return base::flat_set(list.begin(), list.end());
}();
diff --git a/Telegram/SourceFiles/data/data_drafts.cpp b/Telegram/SourceFiles/data/data_drafts.cpp
index 474822a48..0a6d64122 100644
--- a/Telegram/SourceFiles/data/data_drafts.cpp
+++ b/Telegram/SourceFiles/data/data_drafts.cpp
@@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_drafts.h"
#include "api/api_text_entities.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "chat_helpers/message_field.h"
#include "history/history.h"
#include "history/history_widget.h"
diff --git a/Telegram/SourceFiles/data/data_forum.cpp b/Telegram/SourceFiles/data/data_forum.cpp
index 507f54478..361135d88 100644
--- a/Telegram/SourceFiles/data/data_forum.cpp
+++ b/Telegram/SourceFiles/data/data_forum.cpp
@@ -24,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "core/application.h"
#include "ui/layers/generic_box.h"
-#include "ui/widgets/input_fields.h"
+#include "ui/widgets/fields/input_field.h"
#include "storage/storage_facade.h"
#include "storage/storage_shared_media.h"
#include "window/window_session_controller.h"
diff --git a/Telegram/SourceFiles/data/data_msg_id.h b/Telegram/SourceFiles/data/data_msg_id.h
index 49357d88f..6a9bc0d1c 100644
--- a/Telegram/SourceFiles/data/data_msg_id.h
+++ b/Telegram/SourceFiles/data/data_msg_id.h
@@ -73,7 +73,7 @@ struct FullReplyTo {
FullStoryId storyId;
[[nodiscard]] bool valid() const {
- return msgId || storyId;
+ return msgId || (storyId && peerIsUser(storyId.peer));
}
explicit operator bool() const {
return valid();
diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp
index c096853c6..00c183161 100644
--- a/Telegram/SourceFiles/data/data_peer.cpp
+++ b/Telegram/SourceFiles/data/data_peer.cpp
@@ -898,6 +898,33 @@ bool PeerData::isRepliesChat() const {
: kTestId) == id;
}
+bool PeerData::sharedMediaInfo() const {
+ return isSelf() || isRepliesChat();
+}
+
+bool PeerData::hasStoriesHidden() const {
+ if (const auto user = asUser()) {
+ return user->hasStoriesHidden();
+ } else if (const auto channel = asChannel()) {
+ return channel->hasStoriesHidden();
+ }
+ return false;
+}
+
+void PeerData::setStoriesHidden(bool hidden) {
+ if (const auto user = asUser()) {
+ user->setFlags(hidden
+ ? (user->flags() | UserDataFlag::StoriesHidden)
+ : (user->flags() & ~UserDataFlag::StoriesHidden));
+ } else if (const auto channel = asChannel()) {
+ channel->setFlags(hidden
+ ? (channel->flags() | ChannelDataFlag::StoriesHidden)
+ : (channel->flags() & ~ChannelDataFlag::StoriesHidden));
+ } else {
+ Unexpected("PeerData::setStoriesHidden for non-user/non-channel.");
+ }
+}
+
Data::Forum *PeerData::forum() const {
if (const auto channel = asChannel()) {
return channel->forum();
@@ -1108,6 +1135,34 @@ const Data::WallPaper *PeerData::wallPaper() const {
return _wallPaper.get();
}
+bool PeerData::hasActiveStories() const {
+ if (const auto user = asUser()) {
+ return user->hasActiveStories();
+ } else if (const auto channel = asChannel()) {
+ return channel->hasActiveStories();
+ }
+ return false;
+}
+
+bool PeerData::hasUnreadStories() const {
+ if (const auto user = asUser()) {
+ return user->hasUnreadStories();
+ } else if (const auto channel = asChannel()) {
+ return channel->hasUnreadStories();
+ }
+ return false;
+}
+
+void PeerData::setStoriesState(StoriesState state) {
+ if (const auto user = asUser()) {
+ return user->setStoriesState(state);
+ } else if (const auto channel = asChannel()) {
+ return channel->setStoriesState(state);
+ } else {
+ Unexpected("PeerData::setStoriesState for non-user/non-channel.");
+ }
+}
+
void PeerData::setIsBlocked(bool is) {
const auto status = is
? BlockStatus::Blocked
diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h
index 17b19bf63..73c3d85f8 100644
--- a/Telegram/SourceFiles/data/data_peer.h
+++ b/Telegram/SourceFiles/data/data_peer.h
@@ -183,9 +183,9 @@ public:
[[nodiscard]] bool isForum() const;
[[nodiscard]] bool isGigagroup() const;
[[nodiscard]] bool isRepliesChat() const;
- [[nodiscard]] bool sharedMediaInfo() const {
- return isSelf() || isRepliesChat();
- }
+ [[nodiscard]] bool sharedMediaInfo() const;
+ [[nodiscard]] bool hasStoriesHidden() const;
+ void setStoriesHidden(bool hidden);
[[nodiscard]] bool isNotificationsUser() const {
return (id == peerFromUser(333000))
@@ -407,6 +407,16 @@ public:
void setWallPaper(std::optional paper);
[[nodiscard]] const Data::WallPaper *wallPaper() const;
+ enum class StoriesState {
+ Unknown,
+ None,
+ HasRead,
+ HasUnread,
+ };
+ [[nodiscard]] bool hasActiveStories() const;
+ [[nodiscard]] bool hasUnreadStories() const;
+ void setStoriesState(StoriesState state);
+
const PeerId id;
MTPinputPeer input = MTP_inputPeerEmpty();
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index 207b786ef..a941f2b73 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -919,7 +919,17 @@ not_null Session::processChat(const MTPChat &data) {
| Flag::NoForwards
| Flag::JoinToWrite
| Flag::RequestToJoin
- | Flag::Forum;
+ | Flag::Forum
+ | ((!minimal && !data.is_stories_hidden_min())
+ ? Flag::StoriesHidden
+ : Flag());
+ const auto storiesState = minimal
+ ? std::optional()
+ : data.is_stories_unavailable()
+ ? Data::Stories::PeerSourceState()
+ : !data.vstories_max_id()
+ ? std::optional()
+ : stories().peerSourceState(channel, data.vstories_max_id()->v);
const auto flagsSet = (data.is_broadcast() ? Flag::Broadcast : Flag())
| (data.is_verified() ? Flag::Verified : Flag())
| (data.is_scam() ? Flag::Scam : Flag())
@@ -945,8 +955,20 @@ not_null Session::processChat(const MTPChat &data) {
| (data.is_join_request() ? Flag::RequestToJoin : Flag())
| ((data.is_forum() && data.is_megagroup())
? Flag::Forum
+ : Flag())
+ | ((!minimal
+ && !data.is_stories_hidden_min()
+ && data.is_stories_hidden())
+ ? Flag::StoriesHidden
: Flag());
channel->setFlags((channel->flags() & ~flagsMask) | flagsSet);
+ if (!minimal && storiesState) {
+ result->setStoriesState(!storiesState->maxId
+ ? UserData::StoriesState::None
+ : (storiesState->maxId > storiesState->readTill)
+ ? UserData::StoriesState::HasUnread
+ : UserData::StoriesState::HasRead);
+ }
channel->setPhoto(data.vphoto());
@@ -2699,7 +2721,7 @@ void Session::checkSelfDestructItems() {
auto nextDestructIn = crl::time(0);
for (auto i = _selfDestructItems.begin(); i != _selfDestructItems.cend();) {
if (const auto item = message(*i)) {
- if (auto destructIn = item->getSelfDestructIn(now)) {
+ if (const auto destructIn = item->getSelfDestructIn(now)) {
if (nextDestructIn > 0) {
accumulate_min(nextDestructIn, destructIn);
} else {
@@ -3425,12 +3447,12 @@ void Session::webpageApplyFields(
for (const auto &attribute : attributes->v) {
attribute.match([&](const MTPDwebPageAttributeStory &data) {
storyId = FullStoryId{
- peerFromUser(data.vuser_id()),
+ peerFromMTP(data.vpeer()),
data.vid().v,
};
if (const auto embed = data.vstory()) {
story = stories().applyFromWebpage(
- peerFromUser(data.vuser_id()),
+ peerFromMTP(data.vpeer()),
*embed);
} else if (const auto maybe = stories().lookup(storyId)) {
story = *maybe;
diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.cpp b/Telegram/SourceFiles/data/data_sponsored_messages.cpp
index 1f19c9264..d22cf976a 100644
--- a/Telegram/SourceFiles/data/data_sponsored_messages.cpp
+++ b/Telegram/SourceFiles/data/data_sponsored_messages.cpp
@@ -301,7 +301,7 @@ void SponsoredMessages::append(
: ImageWithLocation{};
return SponsoredFrom{
.title = qs(data.vsite_name()),
- .isExternalLink = true,
+ .externalLink = externalLink,
.userpic = std::move(userpic),
.isForceUserpicDisplay = message.data().is_show_peer_photo(),
};
diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.h b/Telegram/SourceFiles/data/data_sponsored_messages.h
index e9cd46b45..2c8b1e72b 100644
--- a/Telegram/SourceFiles/data/data_sponsored_messages.h
+++ b/Telegram/SourceFiles/data/data_sponsored_messages.h
@@ -31,7 +31,7 @@ struct SponsoredFrom {
bool isBot = false;
bool isExactPost = false;
bool isRecommended = false;
- bool isExternalLink = false;
+ QString externalLink;
ImageWithLocation userpic;
bool isForceUserpicDisplay = false;
};
diff --git a/Telegram/SourceFiles/data/data_stories.cpp b/Telegram/SourceFiles/data/data_stories.cpp
index d8197de20..0f0ff8b92 100644
--- a/Telegram/SourceFiles/data/data_stories.cpp
+++ b/Telegram/SourceFiles/data/data_stories.cpp
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "core/application.h"
#include "data/data_changes.h"
+#include "data/data_channel.h"
#include "data/data_document.h"
#include "data/data_folder.h"
#include "data/data_photo.h"
@@ -82,11 +83,11 @@ using UpdateFlag = StoryUpdate::Flag;
StoriesSourceInfo StoriesSource::info() const {
return {
- .id = user->id,
+ .id = peer->id,
.last = ids.empty() ? 0 : ids.back().date,
.count = uint32(std::min(int(ids.size()), kMaxSegmentsCount)),
.unreadCount = uint32(std::min(unreadCount(), kMaxSegmentsCount)),
- .premium = user->isPremium() ? 1U : 0U,
+ .premium = (peer->isUser() && peer->asUser()->isPremium()) ? 1U : 0,
};
}
@@ -110,6 +111,31 @@ Stories::Stories(not_null owner)
, _incrementViewsTimer([=] { sendIncrementViewsRequests(); })
, _pollingTimer([=] { sendPollingRequests(); })
, _pollingViewsTimer([=] { sendPollingViewsRequests(); }) {
+ crl::on_main(this, [=] {
+ session().changes().peerUpdates(
+ Data::PeerUpdate::Flag::Rights
+ ) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
+ const auto channel = update.peer->asChannel();
+ if (!channel) {
+ return;
+ } else if (!channel->canEditStories()) {
+ const auto peerId = channel->id;
+ const auto i = _peersWithDeletedStories.find(peerId);
+ if (i != end(_peersWithDeletedStories)) {
+ _peersWithDeletedStories.erase(i);
+ for (auto j = begin(_deleted); j != end(_deleted);) {
+ if (j->peer == peerId) {
+ j = _deleted.erase(j);
+ } else {
+ ++j;
+ }
+ }
+ }
+ } else {
+ clearArchive(channel);
+ }
+ }, _lifetime);
+ });
}
Stories::~Stories() {
@@ -133,10 +159,10 @@ void Stories::apply(const MTPDupdateStory &data) {
return;
}
- const auto peerId = peerFromUser(data.vuser_id());
- const auto user = not_null(_owner->peer(peerId)->asUser());
+ const auto peerId = peerFromMTP(data.vpeer());
+ const auto peer = _owner->peer(peerId);
const auto now = base::unixtime::now();
- const auto idDates = parseAndApply(user, data.vstory(), now);
+ const auto idDates = parseAndApply(peer, data.vstory(), now);
if (!idDates) {
return;
}
@@ -147,7 +173,7 @@ void Stories::apply(const MTPDupdateStory &data) {
}
const auto i = _all.find(peerId);
if (i == end(_all)) {
- requestUserStories(user);
+ requestPeerStories(peer);
return;
} else if (i->second.ids.contains(idDates)) {
return;
@@ -155,8 +181,8 @@ void Stories::apply(const MTPDupdateStory &data) {
const auto wasInfo = i->second.info();
i->second.ids.emplace(idDates);
const auto nowInfo = i->second.info();
- if (user->isSelf() && i->second.readTill < idDates.id) {
- _readTill[user->id] = i->second.readTill = idDates.id;
+ if (peer->isSelf() && i->second.readTill < idDates.id) {
+ _readTill[peerId] = i->second.readTill = idDates.id;
}
if (wasInfo == nowInfo) {
return;
@@ -172,13 +198,13 @@ void Stories::apply(const MTPDupdateStory &data) {
sort(list);
}
};
- if (user->hasStoriesHidden()) {
+ if (peer->hasStoriesHidden()) {
refreshInList(StorySourcesList::Hidden);
} else {
refreshInList(StorySourcesList::NotHidden);
}
_sourceChanged.fire_copy(peerId);
- updateUserStoriesState(user);
+ updatePeerStoriesState(peer);
}
void Stories::apply(const MTPDupdateReadStories &data) {
@@ -189,7 +215,7 @@ void Stories::apply(const MTPDupdateReadStories &data) {
return;
}
- bumpReadTill(peerFromUser(data.vuser_id()), data.vmax_id().v);
+ bumpReadTill(peerFromMTP(data.vpeer()), data.vmax_id().v);
}
void Stories::apply(const MTPStoriesStealthMode &stealthMode) {
@@ -200,20 +226,19 @@ void Stories::apply(const MTPStoriesStealthMode &stealthMode) {
};
}
-void Stories::apply(not_null peer, const MTPUserStories *data) {
+void Stories::apply(not_null peer, const MTPPeerStories *data) {
// AyuGram disableStories
const auto settings = &AyuSettings::getInstance();
if (settings->disableStories)
{
return;
}
-
if (!data) {
applyDeletedFromSources(peer->id, StorySourcesList::NotHidden);
applyDeletedFromSources(peer->id, StorySourcesList::Hidden);
_all.erase(peer->id);
_sourceChanged.fire_copy(peer->id);
- updateUserStoriesState(peer);
+ updatePeerStoriesState(peer);
} else {
parseAndApply(*data);
}
@@ -237,10 +262,10 @@ Story *Stories::applyFromWebpage(PeerId peerId, const MTPstoryItem &story) {
return value ? value->get() : nullptr;
}
-void Stories::requestUserStories(
- not_null user,
+void Stories::requestPeerStories(
+ not_null peer,
Fn done) {
- const auto [i, ok] = _requestingUserStories.emplace(user);
+ const auto [i, ok] = _requestingPeerStories.emplace(peer);
if (done) {
i->second.push_back(std::move(done));
}
@@ -248,22 +273,23 @@ void Stories::requestUserStories(
return;
}
const auto finish = [=] {
- if (const auto callbacks = _requestingUserStories.take(user)) {
+ if (const auto callbacks = _requestingPeerStories.take(peer)) {
for (const auto &callback : *callbacks) {
callback();
}
}
};
- _owner->session().api().request(MTPstories_GetUserStories(
- user->inputUser
- )).done([=](const MTPstories_UserStories &result) {
+ _owner->session().api().request(MTPstories_GetPeerStories(
+ peer->input
+ )).done([=](const MTPstories_PeerStories &result) {
const auto &data = result.data();
_owner->processUsers(data.vusers());
+ _owner->processChats(data.vchats());
parseAndApply(data.vstories());
finish();
}).fail([=] {
- applyDeletedFromSources(user->id, StorySourcesList::NotHidden);
- applyDeletedFromSources(user->id, StorySourcesList::Hidden);
+ applyDeletedFromSources(peer->id, StorySourcesList::NotHidden);
+ applyDeletedFromSources(peer->id, StorySourcesList::Hidden);
finish();
}).send();
}
@@ -323,33 +349,63 @@ void Stories::processExpired() {
}
}
-void Stories::parseAndApply(const MTPUserStories &stories) {
+Stories::Set *Stories::lookupArchive(not_null peer) {
+ const auto peerId = peer->id;
+ if (hasArchive(peer)) {
+ const auto i = _archive.find(peerId);
+ return (i != end(_archive))
+ ? &i->second
+ : &_archive.emplace(peerId, Set()).first->second;
+ }
+ clearArchive(peer);
+ return nullptr;
+}
+
+void Stories::clearArchive(not_null peer) {
+ const auto peerId = peer->id;
+ const auto i = _archive.find(peerId);
+ if (i == end(_archive)) {
+ return;
+ }
+ auto archive = base::take(i->second);
+ _archive.erase(i);
+ for (const auto &id : archive.ids.list) {
+ if (const auto story = lookup({ peerId, id })) {
+ if ((*story)->expired() && !(*story)->pinned()) {
+ applyDeleted(peer, id);
+ }
+ }
+ }
+ _archiveChanged.fire_copy(peerId);
+}
+
+void Stories::parseAndApply(const MTPPeerStories &stories) {
const auto &data = stories.data();
- const auto peerId = peerFromUser(data.vuser_id());
+ const auto peerId = peerFromMTP(data.vpeer());
const auto already = _readTill.find(peerId);
const auto readTill = std::max(
data.vmax_read_id().value_or_empty(),
(already != end(_readTill) ? already->second : 0));
- const auto user = _owner->peer(peerId)->asUser();
+ const auto peer = _owner->peer(peerId);
auto result = StoriesSource{
- .user = user,
+ .peer = peer,
.readTill = readTill,
- .hidden = user->hasStoriesHidden(),
+ .hidden = peer->hasStoriesHidden(),
};
const auto &list = data.vstories().v;
const auto now = base::unixtime::now();
result.ids.reserve(list.size());
for (const auto &story : list) {
- if (const auto id = parseAndApply(result.user, story, now)) {
+ if (const auto id = parseAndApply(result.peer, story, now)) {
result.ids.emplace(id);
}
}
if (result.ids.empty()) {
applyDeletedFromSources(peerId, StorySourcesList::NotHidden);
applyDeletedFromSources(peerId, StorySourcesList::Hidden);
- user->setStoriesState(UserData::StoriesState::None);
+ peer->setStoriesState(PeerData::StoriesState::None);
return;
- } else if (user->isSelf()) {
+ } else if (peer->isSelf()) {
result.readTill = result.ids.back().id;
}
_readTill[peerId] = result.readTill;
@@ -377,11 +433,13 @@ void Stories::parseAndApply(const MTPUserStories &stories) {
}
sort(list);
};
- if (result.user->isSelf()
- || result.user->isBot()
- || result.user->isServiceUser()
- || result.user->isContact()) {
- const auto hidden = result.user->hasStoriesHidden();
+ if (result.peer->isSelf()
+ || (result.peer->isChannel() && result.peer->asChannel()->amIn())
+ || (result.peer->isUser()
+ && (result.peer->asUser()->isBot()
+ || result.peer->asUser()->isContact()))
+ || result.peer->isServiceUser()) {
+ const auto hidden = result.peer->hasStoriesHidden();
using List = StorySourcesList;
add(hidden ? List::Hidden : List::NotHidden);
applyDeletedFromSources(
@@ -392,7 +450,7 @@ void Stories::parseAndApply(const MTPUserStories &stories) {
applyDeletedFromSources(peerId, StorySourcesList::Hidden);
}
_sourceChanged.fire_copy(peerId);
- updateUserStoriesState(result.user);
+ updatePeerStoriesState(result.peer);
}
Story *Stories::parseAndApply(
@@ -405,7 +463,7 @@ Story *Stories::parseAndApply(
}
const auto expires = data.vexpire_date().v;
const auto expired = (expires <= now);
- if (expired && !data.is_pinned() && !peer->isSelf()) {
+ if (expired && !data.is_pinned() && !hasArchive(peer)) {
return nullptr;
}
const auto id = data.vid().v;
@@ -441,13 +499,13 @@ Story *Stories::parseAndApply(
now
)).first->second.get();
- if (peer->isSelf()) {
- const auto added = _archive.list.emplace(id).second;
+ if (const auto archive = lookupArchive(peer)) {
+ const auto added = archive->ids.list.emplace(id).second;
if (added) {
- if (_archiveTotal >= 0 && id > _archiveLastId) {
- ++_archiveTotal;
+ if (archive->total >= 0 && id > archive->lastId) {
+ ++archive->total;
}
- _archiveChanged.fire({});
+ _archiveChanged.fire_copy(peer->id);
}
}
@@ -473,7 +531,7 @@ StoryIdDates Stories::parseAndApply(
if (const auto story = parseAndApply(peer, data, now)) {
return story->idDates();
}
- applyDeleted({ peer->id, data.vid().v });
+ applyDeleted(peer, data.vid().v);
return StoryIdDates();
}, [&](const MTPDstoryItemSkipped &data) {
const auto expires = data.vexpire_date().v;
@@ -481,8 +539,8 @@ StoryIdDates Stories::parseAndApply(
const auto fullId = FullStoryId{ peer->id, data.vid().v };
if (!expired) {
registerExpiring(expires, fullId);
- } else if (!peer->isSelf()) {
- applyDeleted(fullId);
+ } else if (!hasArchive(peer)) {
+ applyDeleted(peer, data.vid().v);
return StoryIdDates();
} else {
_expiring.remove(expires, fullId);
@@ -494,7 +552,7 @@ StoryIdDates Stories::parseAndApply(
data.vexpire_date().v,
};
}, [&](const MTPDstoryItemDeleted &data) {
- applyDeleted({ peer->id, data.vid().v });
+ applyDeleted(peer, data.vid().v);
return StoryIdDates();
});
}
@@ -571,9 +629,10 @@ void Stories::loadMore(StorySourcesList list) {
result.match([&](const MTPDstories_allStories &data) {
_owner->processUsers(data.vusers());
+ _owner->processChats(data.vchats());
_sourcesStates[index] = qs(data.vstate());
_sourcesLoaded[index] = !data.is_has_more();
- for (const auto &single : data.vuser_stories().v) {
+ for (const auto &single : data.vpeer_stories().v) {
parseAndApply(single);
}
}, [](const MTPDstories_allStoriesNotModified &) {
@@ -608,8 +667,8 @@ void Stories::preloadListsMore() {
loadMore(StorySourcesList::NotHidden);
} else if (!countLoaded(StorySourcesList::Hidden)) {
loadMore(StorySourcesList::Hidden);
- } else if (!archiveCountKnown()) {
- archiveLoadMore();
+ } else if (!archiveCountKnown(_owner->session().userPeerId())) {
+ archiveLoadMore(_owner->session().userPeerId());
}
}
@@ -686,18 +745,15 @@ void Stories::sendResolveRequests() {
crl::on_main(&session(), [=] { sendResolveRequests(); });
}
};
- const auto user = _owner->session().data().peer(peerId)->asUser();
- if (!user) {
- finish(peerId);
- continue;
- }
+ const auto peer = _owner->session().data().peer(peerId);
api->request(MTPstories_GetStoriesByID(
- user->inputUser,
+ peer->input,
MTP_vector(prepared)
)).done([=](const MTPstories_Stories &result) {
owner().processUsers(result.data().vusers());
- processResolvedStories(user, result.data().vstories().v);
- finish(user->id);
+ owner().processChats(result.data().vchats());
+ processResolvedStories(peer, result.data().vstories().v);
+ finish(peer->id);
}).fail([=] {
finish(peerId);
}).send();
@@ -711,12 +767,12 @@ void Stories::processResolvedStories(
for (const auto &item : list) {
item.match([&](const MTPDstoryItem &data) {
if (!parseAndApply(peer, data, now)) {
- applyDeleted({ peer->id, data.vid().v });
+ applyDeleted(peer, data.vid().v);
}
}, [&](const MTPDstoryItemSkipped &data) {
LOG(("API Error: Unexpected storyItemSkipped in resolve."));
}, [&](const MTPDstoryItemDeleted &data) {
- applyDeleted({ peer->id, data.vid().v });
+ applyDeleted(peer, data.vid().v);
});
}
}
@@ -727,19 +783,29 @@ void Stories::finalizeResolve(FullStoryId id) {
LOG(("API Error: Could not resolve story %1_%2"
).arg(id.peer.value
).arg(id.story));
- applyDeleted(id);
+ applyDeleted(_owner->peer(id.peer), id.story);
}
}
-void Stories::applyDeleted(FullStoryId id) {
- applyRemovedFromActive(id);
+void Stories::applyDeleted(not_null peer, StoryId id) {
+ const auto fullId = FullStoryId{ peer->id, id };
+ applyRemovedFromActive(fullId);
- _deleted.emplace(id);
- const auto i = _stories.find(id.peer);
+ if (const auto channel = peer->asChannel()) {
+ if (!hasArchive(channel)) {
+ _peersWithDeletedStories.emplace(channel->id);
+ }
+ }
+
+ _deleted.emplace(fullId);
+ const auto peerId = peer->id;
+ const auto i = _stories.find(peerId);
if (i != end(_stories)) {
- const auto j = i->second.find(id.story);
+ const auto j = i->second.find(id);
if (j != end(i->second)) {
- const auto &story = _deletingStories[id] = std::move(j->second);
+ const auto &story
+ = _deletingStories[fullId]
+ = std::move(j->second);
_expiring.remove(story->expires(), story->fullId());
i->second.erase(j);
@@ -747,32 +813,37 @@ void Stories::applyDeleted(FullStoryId id) {
story.get(),
UpdateFlag::Destroyed);
removeDependencyStory(story.get());
- if (id.peer == session().userPeerId()
- && _archive.list.remove(id.story)) {
- if (_archiveTotal > 0) {
- --_archiveTotal;
- }
- _archiveChanged.fire({});
- }
- if (story->pinned()) {
- if (const auto k = _saved.find(id.peer); k != end(_saved)) {
- const auto saved = &k->second;
- if (saved->ids.list.remove(id.story)) {
- if (saved->total > 0) {
- --saved->total;
+ if (hasArchive(story->peer())) {
+ if (const auto k = _archive.find(peerId)
+ ; k != end(_archive)) {
+ const auto archive = &k->second;
+ if (archive->ids.list.remove(id)) {
+ if (archive->total > 0) {
+ --archive->total;
}
- _savedChanged.fire_copy(id.peer);
+ _archiveChanged.fire_copy(peerId);
}
}
}
- if (_preloading && _preloading->id() == id) {
- _preloading = nullptr;
- preloadFinished(id);
+ if (story->pinned()) {
+ if (const auto k = _saved.find(peerId); k != end(_saved)) {
+ const auto saved = &k->second;
+ if (saved->ids.list.remove(id)) {
+ if (saved->total > 0) {
+ --saved->total;
+ }
+ _savedChanged.fire_copy(peerId);
+ }
+ }
}
- _owner->refreshStoryItemViews(id);
+ if (_preloading && _preloading->id() == fullId) {
+ _preloading = nullptr;
+ preloadFinished(fullId);
+ }
+ _owner->refreshStoryItemViews(fullId);
Assert(!_pollingSettings.contains(story.get()));
- if (const auto j = _items.find(id.peer); j != end(_items)) {
- const auto k = j->second.find(id.story);
+ if (const auto j = _items.find(peerId); j != end(_items)) {
+ const auto k = j->second.find(id);
if (k != end(j->second)) {
Assert(!k->second.lock());
j->second.erase(k);
@@ -784,7 +855,7 @@ void Stories::applyDeleted(FullStoryId id) {
if (i->second.empty()) {
_stories.erase(i);
}
- _deletingStories.remove(id);
+ _deletingStories.remove(fullId);
}
}
}
@@ -792,8 +863,8 @@ void Stories::applyDeleted(FullStoryId id) {
void Stories::applyExpired(FullStoryId id) {
if (const auto maybeStory = lookup(id)) {
const auto story = *maybeStory;
- if (!story->peer()->isSelf() && !story->pinned()) {
- applyDeleted(id);
+ if (!hasArchive(story->peer()) && !story->pinned()) {
+ applyDeleted(story->peer(), id.story);
return;
}
}
@@ -818,14 +889,14 @@ void Stories::applyRemovedFromActive(FullStoryId id) {
const auto j = i->second.ids.lower_bound(StoryIdDates{ id.story });
if (j != end(i->second.ids) && j->id == id.story) {
i->second.ids.erase(j);
- const auto user = i->second.user;
+ const auto peer = i->second.peer;
if (i->second.ids.empty()) {
_all.erase(i);
removeFromList(StorySourcesList::NotHidden);
removeFromList(StorySourcesList::Hidden);
}
_sourceChanged.fire_copy(id.peer);
- updateUserStoriesState(user);
+ updatePeerStoriesState(peer);
}
}
}
@@ -913,7 +984,7 @@ void Stories::sendReaction(FullStoryId id, Data::ReactionId reaction) {
const auto api = &session().api();
api->request(MTPstories_SendReaction(
MTP_flags(0),
- story->peer()->asUser()->inputUser,
+ story->peer()->input,
MTP_int(id.story),
ReactionToMTP(reaction)
)).send();
@@ -1101,7 +1172,7 @@ bool Stories::bumpReadTill(PeerId peerId, StoryId maxReadTill) {
if (till < maxReadTill) {
const auto from = till;
till = maxReadTill;
- updateUserStoriesState(_owner->peer(peerId));
+ updatePeerStoriesState(_owner->peer(peerId));
const auto i = _stories.find(peerId);
if (i != end(_stories)) {
refreshItems = ranges::make_subrange(
@@ -1144,19 +1215,16 @@ void Stories::toggleHidden(
PeerId peerId,
bool hidden,
std::shared_ptr show) {
- const auto user = _owner->peer(peerId)->asUser();
- Assert(user != nullptr);
- if (user->hasStoriesHidden() != hidden) {
- user->setFlags(hidden
- ? (user->flags() | UserDataFlag::StoriesHidden)
- : (user->flags() & ~UserDataFlag::StoriesHidden));
- session().api().request(MTPcontacts_ToggleStoriesHidden(
- user->inputUser,
+ const auto peer = _owner->peer(peerId);
+ if (peer->hasStoriesHidden() != hidden) {
+ peer->setStoriesHidden(hidden);
+ session().api().request(MTPstories_TogglePeerStoriesHidden(
+ peer->input,
MTP_bool(hidden)
)).send();
}
- const auto name = user->shortName();
+ const auto name = peer->shortName();
const auto guard = gsl::finally([&] {
if (show) {
const auto phrase = hidden
@@ -1213,8 +1281,6 @@ void Stories::toggleHidden(
void Stories::sendMarkAsReadRequest(
not_null peer,
StoryId tillId) {
- Expects(peer->isUser());
-
// AyuGram sendReadStories
const auto settings = &AyuSettings::getInstance();
@@ -1241,7 +1307,7 @@ void Stories::sendMarkAsReadRequest(
};
const auto api = &_owner->session().api();
api->request(MTPstories_ReadStories(
- peer->asUser()->inputUser,
+ peer->input,
MTP_int(tillId)
)).done(finish).fail(finish).send();
}
@@ -1265,7 +1331,7 @@ void Stories::sendMarkAsReadRequests() {
}
const auto j = _all.find(peerId);
if (j != end(_all)) {
- sendMarkAsReadRequest(j->second.user, j->second.readTill);
+ sendMarkAsReadRequest(j->second.peer, j->second.readTill);
}
i = _markReadPending.erase(i);
}
@@ -1313,7 +1379,7 @@ void Stories::sendIncrementViewsRequests() {
checkQuitPreventFinished();
};
api->request(MTPstories_IncrementStoryViews(
- _owner->peer(peer)->asUser()->inputUser,
+ _owner->peer(peer)->input,
MTP_vector(std::move(ids))
)).done(finish).fail(finish).send();
_incrementViewsPending.remove(peer);
@@ -1321,10 +1387,14 @@ void Stories::sendIncrementViewsRequests() {
}
void Stories::loadViewsSlice(
+ not_null peer,
StoryId id,
QString offset,
Fn done) {
- if (_viewsStoryId == id
+ Expects(peer->isSelf() || !done);
+
+ if (_viewsStoryPeer == peer
+ && _viewsStoryId == id
&& _viewsOffset == offset
&& (!offset.isEmpty() || _viewsRequestId)) {
if (_viewsRequestId) {
@@ -1332,20 +1402,32 @@ void Stories::loadViewsSlice(
}
return;
}
+ _viewsStoryPeer = peer;
_viewsStoryId = id;
_viewsOffset = offset;
_viewsDone = std::move(done);
- const auto api = &_owner->session().api();
- const auto perPage = _viewsDone ? kViewsPerPage : kPollingViewsPerPage;
- api->request(_viewsRequestId).cancel();
+ if (peer->isSelf()) {
+ sendViewsSliceRequest();
+ } else {
+ sendViewsCountsRequest();
+ }
+}
+
+void Stories::sendViewsSliceRequest() {
+ Expects(_viewsStoryPeer != nullptr);
+ Expects(_viewsStoryPeer->isSelf());
+
using Flag = MTPstories_GetStoryViewsList::Flag;
+ const auto api = &_owner->session().api();
+ _owner->session().api().request(_viewsRequestId).cancel();
_viewsRequestId = api->request(MTPstories_GetStoryViewsList(
MTP_flags(Flag::f_reactions_first),
+ _viewsStoryPeer->input,
MTPstring(), // q
- MTP_int(id),
- MTP_string(offset),
- MTP_int(perPage)
+ MTP_int(_viewsStoryId),
+ MTP_string(_viewsOffset),
+ MTP_int(_viewsDone ? kViewsPerPage : kPollingViewsPerPage)
)).done([=](const MTPstories_StoryViewsList &result) {
_viewsRequestId = 0;
@@ -1384,29 +1466,72 @@ void Stories::loadViewsSlice(
}).send();
}
-const StoriesIds &Stories::archive() const {
- return _archive;
+void Stories::sendViewsCountsRequest() {
+ Expects(_viewsStoryPeer != nullptr);
+ Expects(!_viewsDone);
+
+ const auto api = &_owner->session().api();
+ _owner->session().api().request(_viewsRequestId).cancel();
+ _viewsRequestId = api->request(MTPstories_GetStoriesViews(
+ _viewsStoryPeer->input,
+ MTP_vector(1, MTP_int(_viewsStoryId))
+ )).done([=](const MTPstories_StoryViews &result) {
+ _viewsRequestId = 0;
+
+ const auto &data = result.data();
+ _owner->processUsers(data.vusers());
+ if (data.vviews().v.size() == 1) {
+ const auto fullId = FullStoryId{
+ _viewsStoryPeer->id,
+ _viewsStoryId,
+ };
+ if (const auto story = lookup(fullId)) {
+ (*story)->applyViewsCounts(data.vviews().v.front().data());
+ }
+ }
+ }).fail([=] {
+ _viewsRequestId = 0;
+ }).send();
}
-rpl::producer<> Stories::archiveChanged() const {
+bool Stories::hasArchive(not_null peer) const {
+ if (peer->isSelf()) {
+ return true;
+ } else if (const auto channel = peer->asChannel()) {
+ return channel->canEditStories();
+ }
+ return false;
+}
+
+const StoriesIds &Stories::archive(PeerId peerId) const {
+ static const auto empty = StoriesIds();
+ const auto i = _archive.find(peerId);
+ return (i != end(_archive)) ? i->second.ids : empty;
+}
+
+rpl::producer Stories::archiveChanged() const {
return _archiveChanged.events();
}
-int Stories::archiveCount() const {
- return std::max(_archiveTotal, 0);
+int Stories::archiveCount(PeerId peerId) const {
+ const auto i = _archive.find(peerId);
+ return (i != end(_archive)) ? i->second.total : 0;
}
-bool Stories::archiveCountKnown() const {
- return _archiveTotal >= 0;
+bool Stories::archiveCountKnown(PeerId peerId) const {
+ const auto i = _archive.find(peerId);
+ return (i != end(_archive)) && (i->second.total >= 0);
}
-bool Stories::archiveLoaded() const {
- return _archiveLoaded;
+bool Stories::archiveLoaded(PeerId peerId) const {
+ const auto i = _archive.find(peerId);
+ return (i != end(_archive)) && i->second.loaded;
}
-const StoriesIds *Stories::saved(PeerId peerId) const {
+const StoriesIds &Stories::saved(PeerId peerId) const {
+ static const auto empty = StoriesIds();
const auto i = _saved.find(peerId);
- return (i != end(_saved)) ? &i->second.ids : nullptr;
+ return (i != end(_saved)) ? i->second.ids : empty;
}
rpl::producer Stories::savedChanged() const {
@@ -1428,49 +1553,57 @@ bool Stories::savedLoaded(PeerId peerId) const {
return (i != end(_saved)) && i->second.loaded;
}
-void Stories::archiveLoadMore() {
- if (_archiveRequestId || _archiveLoaded) {
+void Stories::archiveLoadMore(PeerId peerId) {
+ const auto peer = _owner->peer(peerId);
+ const auto archive = lookupArchive(peer);
+ if (!archive || archive->requestId || archive->loaded) {
return;
}
const auto api = &_owner->session().api();
- _archiveRequestId = api->request(MTPstories_GetStoriesArchive(
- MTP_int(_archiveLastId),
- MTP_int(_archiveLastId ? kArchivePerPage : kArchiveFirstPerPage)
+ archive->requestId = api->request(MTPstories_GetStoriesArchive(
+ peer->input,
+ MTP_int(archive->lastId),
+ MTP_int(archive->lastId ? kArchivePerPage : kArchiveFirstPerPage)
)).done([=](const MTPstories_Stories &result) {
- _archiveRequestId = 0;
+ const auto archive = lookupArchive(peer);
+ if (!archive) {
+ return;
+ }
+ archive->requestId = 0;
const auto &data = result.data();
- const auto self = _owner->session().user();
const auto now = base::unixtime::now();
- _archiveTotal = data.vcount().v;
+ archive->total = data.vcount().v;
for (const auto &story : data.vstories().v) {
const auto id = story.match([&](const auto &id) {
return id.vid().v;
});
- _archive.list.emplace(id);
- _archiveLastId = id;
- if (!parseAndApply(self, story, now)) {
- _archive.list.remove(id);
- if (_archiveTotal > 0) {
- --_archiveTotal;
+ archive->ids.list.emplace(id);
+ archive->lastId = id;
+ if (!parseAndApply(peer, story, now)) {
+ archive->ids.list.remove(id);
+ if (archive->total > 0) {
+ --archive->total;
}
}
}
- const auto ids = int(_archive.list.size());
- _archiveLoaded = data.vstories().v.empty();
- _archiveTotal = _archiveLoaded ? ids : std::max(_archiveTotal, ids);
- _archiveChanged.fire({});
+ const auto ids = int(archive->ids.list.size());
+ archive->loaded = data.vstories().v.empty();
+ archive->total = archive->loaded ? ids : std::max(archive->total, ids);
+ _archiveChanged.fire_copy(peerId);
}).fail([=] {
- _archiveRequestId = 0;
- _archiveLoaded = true;
- _archiveTotal = int(_archive.list.size());
- _archiveChanged.fire({});
+ const auto archive = lookupArchive(peer);
+ if (!archive) {
+ return;
+ }
+ archive->requestId = 0;
+ archive->loaded = true;
+ archive->total = int(archive->ids.list.size());
+ _archiveChanged.fire_copy(peerId);
}).send();
}
void Stories::savedLoadMore(PeerId peerId) {
- Expects(peerIsUser(peerId));
-
auto &saved = _saved[peerId];
if (saved.requestId || saved.loaded) {
return;
@@ -1478,7 +1611,7 @@ void Stories::savedLoadMore(PeerId peerId) {
const auto api = &_owner->session().api();
const auto peer = _owner->peer(peerId);
saved.requestId = api->request(MTPstories_GetPinnedStories(
- peer->asUser()->inputUser,
+ peer->input,
MTP_int(saved.lastId),
MTP_int(saved.lastId ? kSavedPerPage : kSavedFirstPerPage)
)).done([=](const MTPstories_Stories &result) {
@@ -1515,34 +1648,39 @@ void Stories::savedLoadMore(PeerId peerId) {
}
void Stories::deleteList(const std::vector &ids) {
+ if (ids.empty()) {
+ return;
+ }
+ const auto peer = session().data().peer(ids.front().peer);
auto list = QVector();
list.reserve(ids.size());
- const auto selfId = session().userPeerId();
for (const auto &id : ids) {
- if (id.peer == selfId) {
+ if (id.peer == peer->id) {
list.push_back(MTP_int(id.story));
}
}
- if (!list.empty()) {
- const auto api = &_owner->session().api();
- api->request(MTPstories_DeleteStories(
- MTP_vector(list)
- )).done([=](const MTPVector &result) {
- for (const auto &id : result.v) {
- applyDeleted({ selfId, id.v });
- }
- }).send();
- }
+ const auto api = &_owner->session().api();
+ api->request(MTPstories_DeleteStories(
+ peer->input,
+ MTP_vector(list)
+ )).done([=](const MTPVector &result) {
+ for (const auto &id : result.v) {
+ applyDeleted(peer, id.v);
+ }
+ }).send();
}
void Stories::togglePinnedList(
const std::vector &ids,
bool pinned) {
+ if (ids.empty()) {
+ return;
+ }
+ const auto peer = session().data().peer(ids.front().peer);
auto list = QVector();
list.reserve(ids.size());
- const auto selfId = session().userPeerId();
for (const auto &id : ids) {
- if (id.peer == selfId) {
+ if (id.peer == peer->id) {
list.push_back(MTP_int(id.story));
}
}
@@ -1551,10 +1689,12 @@ void Stories::togglePinnedList(
}
const auto api = &_owner->session().api();
api->request(MTPstories_TogglePinned(
+ peer->input,
MTP_vector(list),
MTP_bool(pinned)
)).done([=](const MTPVector &result) {
- auto &saved = _saved[selfId];
+ const auto peerId = peer->id;
+ auto &saved = _saved[peerId];
const auto loaded = saved.loaded;
const auto lastId = !saved.ids.list.empty()
? saved.ids.list.back()
@@ -1563,7 +1703,7 @@ void Stories::togglePinnedList(
: std::numeric_limits::max();
auto dirty = false;
for (const auto &id : result.v) {
- if (const auto maybeStory = lookup({ selfId, id.v })) {
+ if (const auto maybeStory = lookup({ peerId, id.v })) {
const auto story = *maybeStory;
story->setPinned(pinned);
if (pinned) {
@@ -1587,9 +1727,9 @@ void Stories::togglePinnedList(
}
}
if (dirty) {
- savedLoadMore(selfId);
+ savedLoadMore(peerId);
} else {
- _savedChanged.fire_copy(selfId);
+ _savedChanged.fire_copy(peerId);
}
}).send();
}
@@ -1684,7 +1824,7 @@ std::optional Stories::peerSourceState(
};
}
requestReadTills();
- _pendingUserStateMaxId[peer] = storyMaxId;
+ _pendingPeerStateMaxId[peer] = storyMaxId;
return std::nullopt;
}
@@ -1693,12 +1833,12 @@ void Stories::requestReadTills() {
return;
}
const auto api = &_owner->session().api();
- _readTillsRequestId = api->request(MTPstories_GetAllReadUserStories(
+ _readTillsRequestId = api->request(MTPstories_GetAllReadPeerStories(
)).done([=](const MTPUpdates &result) {
_readTillReceived = true;
api->applyUpdates(result);
- for (auto &[peer, maxId] : base::take(_pendingUserStateMaxId)) {
- updateUserStoriesState(peer);
+ for (auto &[peer, maxId] : base::take(_pendingPeerStateMaxId)) {
+ updatePeerStoriesState(peer);
}
for (const auto &storyId : base::take(_pendingReadTillItems)) {
_owner->refreshStoryItemViews(storyId);
@@ -1723,7 +1863,7 @@ void Stories::registerPolling(not_null story, Polling polling) {
case Polling::Chat: ++settings.chat; break;
case Polling::Viewer:
++settings.viewer;
- if (story->peer()->isSelf()
+ if ((story->peer()->isSelf() || story->peer()->isChannel())
&& _pollingViews.emplace(story).second) {
sendPollingViewsRequests();
}
@@ -1816,29 +1956,28 @@ void Stories::sendPollingViewsRequests() {
return;
} else if (!_viewsRequestId) {
Assert(_viewsDone == nullptr);
- loadViewsSlice(_pollingViews.front()->id(), QString(), nullptr);
+ const auto story = _pollingViews.front();
+ loadViewsSlice(story->peer(), story->id(), QString(), nullptr);
}
_pollingViewsTimer.callOnce(kPollViewsInterval);
}
-void Stories::updateUserStoriesState(not_null peer) {
+void Stories::updatePeerStoriesState(not_null peer) {
const auto till = _readTill.find(peer->id);
const auto readTill = (till != end(_readTill)) ? till->second : 0;
const auto pendingMaxId = [&] {
- const auto j = _pendingUserStateMaxId.find(peer);
- return (j != end(_pendingUserStateMaxId)) ? j->second : 0;
+ const auto j = _pendingPeerStateMaxId.find(peer);
+ return (j != end(_pendingPeerStateMaxId)) ? j->second : 0;
};
const auto i = _all.find(peer->id);
const auto max = (i != end(_all))
? (i->second.ids.empty() ? 0 : i->second.ids.back().id)
: pendingMaxId();
- if (const auto user = peer->asUser()) {
- user->setStoriesState(!max
- ? UserData::StoriesState::None
- : (max <= readTill)
- ? UserData::StoriesState::HasRead
- : UserData::StoriesState::HasUnread);
- }
+ peer->setStoriesState(!max
+ ? PeerData::StoriesState::None
+ : (max <= readTill)
+ ? PeerData::StoriesState::HasRead
+ : PeerData::StoriesState::HasUnread);
}
void Stories::preloadSourcesChanged(StorySourcesList list) {
diff --git a/Telegram/SourceFiles/data/data_stories.h b/Telegram/SourceFiles/data/data_stories.h
index 8d8b23da4..16cc27d38 100644
--- a/Telegram/SourceFiles/data/data_stories.h
+++ b/Telegram/SourceFiles/data/data_stories.h
@@ -52,7 +52,7 @@ struct StoriesSourceInfo {
};
struct StoriesSource {
- not_null user;
+ not_null peer;
base::flat_set ids;
StoryId readTill = 0;
bool hidden = false;
@@ -148,7 +148,7 @@ public:
void apply(const MTPDupdateStory &data);
void apply(const MTPDupdateReadStories &data);
void apply(const MTPStoriesStealthMode &stealthMode);
- void apply(not_null peer, const MTPUserStories *data);
+ void apply(not_null peer, const MTPPeerStories *data);
Story *applyFromWebpage(PeerId peerId, const MTPstoryItem &story);
void loadAround(FullStoryId id, StoriesContext context);
@@ -178,18 +178,21 @@ public:
static constexpr auto kViewsPerPage = 50;
void loadViewsSlice(
+ not_null peer,
StoryId id,
QString offset,
Fn done);
- [[nodiscard]] const StoriesIds &archive() const;
- [[nodiscard]] rpl::producer<> archiveChanged() const;
- [[nodiscard]] int archiveCount() const;
- [[nodiscard]] bool archiveCountKnown() const;
- [[nodiscard]] bool archiveLoaded() const;
- void archiveLoadMore();
+ [[nodiscard]] bool hasArchive(not_null peer) const;
- [[nodiscard]] const StoriesIds *saved(PeerId peerId) const;
+ [[nodiscard]] const StoriesIds &archive(PeerId peerId) const;
+ [[nodiscard]] rpl::producer archiveChanged() const;
+ [[nodiscard]] int archiveCount(PeerId peerId) const;
+ [[nodiscard]] bool archiveCountKnown(PeerId peerId) const;
+ [[nodiscard]] bool archiveLoaded(PeerId peerId) const;
+ void archiveLoadMore(PeerId peerId);
+
+ [[nodiscard]] const StoriesIds &saved(PeerId peerId) const;
[[nodiscard]] rpl::producer savedChanged() const;
[[nodiscard]] int savedCount(PeerId peerId) const;
[[nodiscard]] bool savedCountKnown(PeerId peerId) const;
@@ -228,8 +231,8 @@ public:
[[nodiscard]] bool registerPolling(FullStoryId id, Polling polling);
void unregisterPolling(FullStoryId id, Polling polling);
- void requestUserStories(
- not_null user,
+ void requestPeerStories(
+ not_null peer,
Fn done = nullptr);
void savedStateChanged(not_null story);
@@ -243,19 +246,20 @@ public:
void sendReaction(FullStoryId id, Data::ReactionId reaction);
private:
- struct Saved {
+ struct Set {
StoriesIds ids;
int total = -1;
StoryId lastId = 0;
bool loaded = false;
mtpRequestId requestId = 0;
};
+
struct PollingSettings {
int chat = 0;
int viewer = 0;
};
- void parseAndApply(const MTPUserStories &stories);
+ void parseAndApply(const MTPPeerStories &stories);
[[nodiscard]] Story *parseAndApply(
not_null peer,
const MTPDstoryItem &data,
@@ -269,9 +273,12 @@ private:
const QVector &list);
void sendResolveRequests();
void finalizeResolve(FullStoryId id);
- void updateUserStoriesState(not_null peer);
+ void updatePeerStoriesState(not_null peer);
- void applyDeleted(FullStoryId id);
+ [[nodiscard]] Set *lookupArchive(not_null peer);
+ void clearArchive(not_null peer);
+
+ void applyDeleted(not_null peer, StoryId id);
void applyExpired(FullStoryId id);
void applyRemovedFromActive(FullStoryId id);
void applyDeletedFromSources(PeerId id, StorySourcesList list);
@@ -309,6 +316,8 @@ private:
TimeId now);
void sendPollingRequests();
void sendPollingViewsRequests();
+ void sendViewsSliceRequest();
+ void sendViewsCountsRequest();
const not_null _owner;
std::unordered_map<
@@ -319,6 +328,7 @@ private:
PeerId,
base::flat_map>> _items;
base::flat_multi_map _expiring;
+ base::flat_set _peersWithDeletedStories;
base::flat_set _deleted;
base::Timer _expireTimer;
bool _expireSchedulePosted = false;
@@ -346,27 +356,24 @@ private:
rpl::event_stream _sourceChanged;
rpl::event_stream _itemsChanged;
- StoriesIds _archive;
- int _archiveTotal = -1;
- StoryId _archiveLastId = 0;
- bool _archiveLoaded = false;
- rpl::event_stream<> _archiveChanged;
- mtpRequestId _archiveRequestId = 0;
+ std::unordered_map _archive;
+ rpl::event_stream _archiveChanged;
- std::unordered_map _saved;
+ std::unordered_map _saved;
rpl::event_stream _savedChanged;
base::flat_set _markReadPending;
base::Timer _markReadTimer;
base::flat_set _markReadRequests;
base::flat_map<
- not_null,
- std::vector>> _requestingUserStories;
+ not_null,
+ std::vector>> _requestingPeerStories;
base::flat_map> _incrementViewsPending;
base::Timer _incrementViewsTimer;
base::flat_set _incrementViewsRequests;
+ PeerData *_viewsStoryPeer = nullptr;
StoryId _viewsStoryId = 0;
QString _viewsOffset;
Fn _viewsDone;
@@ -381,7 +388,7 @@ private:
base::flat_map _readTill;
base::flat_set _pendingReadTillItems;
- base::flat_map, StoryId> _pendingUserStateMaxId;
+ base::flat_map, StoryId> _pendingPeerStateMaxId;
mtpRequestId _readTillsRequestId = 0;
bool _readTillReceived = false;
@@ -392,6 +399,8 @@ private:
rpl::variable _stealthMode;
+ rpl::lifetime _lifetime;
+
};
} // namespace Data
diff --git a/Telegram/SourceFiles/data/data_stories_ids.cpp b/Telegram/SourceFiles/data/data_stories_ids.cpp
index 7506abea5..f8921fe8b 100644
--- a/Telegram/SourceFiles/data/data_stories_ids.cpp
+++ b/Telegram/SourceFiles/data/data_stories_ids.cpp
@@ -32,19 +32,19 @@ rpl::producer SavedStoriesIds(
const auto push = [=] {
state->scheduled = false;
+ const auto peerId = peer->id;
const auto stories = &peer->owner().stories();
- if (!stories->savedCountKnown(peer->id)) {
+ if (!stories->savedCountKnown(peerId)) {
return;
}
- const auto saved = stories->saved(peer->id);
- Assert(saved != nullptr);
- const auto count = stories->savedCount(peer->id);
- const auto around = saved->list.lower_bound(aroundId);
- const auto hasBefore = int(around - begin(saved->list));
- const auto hasAfter = int(end(saved->list) - around);
+ const auto &saved = stories->saved(peerId);
+ const auto count = stories->savedCount(peerId);
+ const auto around = saved.list.lower_bound(aroundId);
+ const auto hasBefore = int(around - begin(saved.list));
+ const auto hasAfter = int(end(saved.list) - around);
if (hasAfter < limit) {
- stories->savedLoadMore(peer->id);
+ stories->savedLoadMore(peerId);
}
const auto takeBefore = std::min(hasBefore, limit);
const auto takeAfter = std::min(hasAfter, limit);
@@ -72,14 +72,15 @@ rpl::producer SavedStoriesIds(
});
};
+ const auto peerId = peer->id;
const auto stories = &peer->owner().stories();
stories->savedChanged(
) | rpl::filter(
- rpl::mappers::_1 == peer->id
+ rpl::mappers::_1 == peerId
) | rpl::start_with_next(schedule, lifetime);
- if (!stories->savedCountKnown(peer->id)) {
- stories->savedLoadMore(peer->id);
+ if (!stories->savedCountKnown(peerId)) {
+ stories->savedLoadMore(peerId);
}
push();
@@ -89,7 +90,7 @@ rpl::producer SavedStoriesIds(
}
rpl::producer ArchiveStoriesIds(
- not_null session,
+ not_null peer,
StoryId aroundId,
int limit) {
return [=](auto consumer) {
@@ -105,18 +106,19 @@ rpl::producer ArchiveStoriesIds(
const auto push = [=] {
state->scheduled = false;
- const auto stories = &session->data().stories();
- if (!stories->archiveCountKnown()) {
+ const auto peerId = peer->id;
+ const auto stories = &peer->owner().stories();
+ if (!stories->archiveCountKnown(peerId)) {
return;
}
- const auto &archive = stories->archive();
- const auto count = stories->archiveCount();
+ const auto &archive = stories->archive(peerId);
+ const auto count = stories->archiveCount(peerId);
const auto i = archive.list.lower_bound(aroundId);
const auto hasBefore = int(i - begin(archive.list));
const auto hasAfter = int(end(archive.list) - i);
if (hasAfter < limit) {
- stories->archiveLoadMore();
+ stories->archiveLoadMore(peerId);
}
const auto takeBefore = std::min(hasBefore, limit);
const auto takeAfter = std::min(hasAfter, limit);
@@ -144,12 +146,13 @@ rpl::producer ArchiveStoriesIds(
});
};
- const auto stories = &session->data().stories();
+ const auto peerId = peer->id;
+ const auto stories = &peer->owner().stories();
stories->archiveChanged(
) | rpl::start_with_next(schedule, lifetime);
- if (!stories->archiveCountKnown()) {
- stories->archiveLoadMore();
+ if (!stories->archiveCountKnown(peerId)) {
+ stories->archiveLoadMore(peerId);
}
push();
diff --git a/Telegram/SourceFiles/data/data_stories_ids.h b/Telegram/SourceFiles/data/data_stories_ids.h
index 26f827f96..6c95c16a9 100644
--- a/Telegram/SourceFiles/data/data_stories_ids.h
+++ b/Telegram/SourceFiles/data/data_stories_ids.h
@@ -25,7 +25,7 @@ using StoriesIdsSlice = AbstractSparseIds>;
int limit);
[[nodiscard]] rpl::producer ArchiveStoriesIds(
- not_null session,
+ not_null peer,
StoryId aroundId,
int limit);
diff --git a/Telegram/SourceFiles/data/data_story.cpp b/Telegram/SourceFiles/data/data_story.cpp
index d5faa4350..7500071c5 100644
--- a/Telegram/SourceFiles/data/data_story.cpp
+++ b/Telegram/SourceFiles/data/data_story.cpp
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_text_entities.h"
#include "data/data_document.h"
#include "data/data_changes.h"
+#include "data/data_channel.h"
#include "data/data_file_origin.h"
#include "data/data_photo.h"
#include "data/data_photo_media.h"
@@ -66,6 +67,25 @@ using UpdateFlag = StoryUpdate::Flag;
});
}, [](const MTPDgeoPointEmpty &) {
});
+ }, [&](const MTPDmediaAreaSuggestedReaction &data) {
+ }, [&](const MTPDinputMediaAreaVenue &data) {
+ LOG(("API Error: Unexpected inputMediaAreaVenue in API data."));
+ });
+ return result;
+}
+
+[[nodiscard]] auto ParseSuggestedReaction(const MTPMediaArea &area)
+-> std::optional {
+ auto result = std::optional