Updated TDesktop sources to 2.7.2

This commit is contained in:
Eric Kotato 2021-04-26 23:45:08 +03:00
commit b4625cadc2
324 changed files with 13799 additions and 3461 deletions

3
.gitattributes vendored
View file

@ -4,3 +4,6 @@
# Ensure diffs have LF endings # Ensure diffs have LF endings
*.diff text eol=lf *.diff text eol=lf
*.bat text eol=crlf *.bat text eol=crlf
# Ensure lottie animations are treated as binary files
*.lottie binary

2
.gitignore vendored
View file

@ -49,3 +49,5 @@ stage
/Linux/ /Linux/
/Telegram/Makefile /Telegram/Makefile
*.*~ *.*~
.idea/
cmake-build-debug/

3
.gitmodules vendored
View file

@ -88,3 +88,6 @@
[submodule "Telegram/ThirdParty/tgcalls"] [submodule "Telegram/ThirdParty/tgcalls"]
path = Telegram/ThirdParty/tgcalls path = Telegram/ThirdParty/tgcalls
url = https://github.com/TelegramMessenger/tgcalls.git url = https://github.com/TelegramMessenger/tgcalls.git
[submodule "Telegram/lib_webview"]
path = Telegram/lib_webview
url = https://github.com/desktop-app/lib_webview.git

View file

@ -19,6 +19,7 @@ add_subdirectory(lib_storage)
add_subdirectory(lib_lottie) add_subdirectory(lib_lottie)
add_subdirectory(lib_qr) add_subdirectory(lib_qr)
add_subdirectory(lib_webrtc) add_subdirectory(lib_webrtc)
add_subdirectory(lib_webview)
add_subdirectory(codegen) add_subdirectory(codegen)
get_filename_component(src_loc SourceFiles REALPATH) get_filename_component(src_loc SourceFiles REALPATH)
@ -26,6 +27,7 @@ get_filename_component(res_loc Resources REALPATH)
include(cmake/telegram_options.cmake) include(cmake/telegram_options.cmake)
include(cmake/lib_ffmpeg.cmake) include(cmake/lib_ffmpeg.cmake)
include(cmake/lib_stripe.cmake)
include(cmake/lib_tgvoip.cmake) include(cmake/lib_tgvoip.cmake)
include(cmake/lib_tgcalls.cmake) include(cmake/lib_tgcalls.cmake)
include(cmake/td_export.cmake) include(cmake/td_export.cmake)
@ -35,6 +37,11 @@ include(cmake/td_scheme.cmake)
include(cmake/td_ui.cmake) include(cmake/td_ui.cmake)
include(cmake/generate_appdata_changelog.cmake) include(cmake/generate_appdata_changelog.cmake)
if (WIN32)
include(cmake/generate_midl.cmake)
generate_midl(Telegram ${src_loc}/platform/win/windows_quiethours.idl)
endif()
set_target_properties(Telegram PROPERTIES AUTOMOC ON AUTORCC ON) set_target_properties(Telegram PROPERTIES AUTOMOC ON AUTORCC ON)
target_link_libraries(Telegram target_link_libraries(Telegram
@ -55,7 +62,9 @@ PRIVATE
desktop-app::lib_storage desktop-app::lib_storage
desktop-app::lib_lottie desktop-app::lib_lottie
desktop-app::lib_qr desktop-app::lib_qr
desktop-app::lib_webview
desktop-app::lib_ffmpeg desktop-app::lib_ffmpeg
desktop-app::lib_stripe
desktop-app::external_lz4 desktop-app::external_lz4
desktop-app::external_rlottie desktop-app::external_rlottie
desktop-app::external_zlib desktop-app::external_zlib
@ -70,7 +79,12 @@ PRIVATE
desktop-app::external_xxhash desktop-app::external_xxhash
) )
if (LINUX) if (WIN32)
target_link_libraries(Telegram
PRIVATE
desktop-app::lib_webview_winrt
)
elseif (LINUX)
target_link_libraries(Telegram target_link_libraries(Telegram
PRIVATE PRIVATE
desktop-app::external_glibmm desktop-app::external_glibmm
@ -249,8 +263,6 @@ PRIVATE
boxes/sessions_box.h boxes/sessions_box.h
boxes/share_box.cpp boxes/share_box.cpp
boxes/share_box.h boxes/share_box.h
boxes/single_choice_box.cpp
boxes/single_choice_box.h
boxes/sticker_set_box.cpp boxes/sticker_set_box.cpp
boxes/sticker_set_box.h boxes/sticker_set_box.h
boxes/stickers_box.cpp boxes/stickers_box.cpp
@ -383,8 +395,6 @@ PRIVATE
data/data_cloud_file.h data/data_cloud_file.h
data/data_cloud_themes.cpp data/data_cloud_themes.cpp
data/data_cloud_themes.h data/data_cloud_themes.h
data/data_countries.cpp
data/data_countries.h
data/data_document.cpp data/data_document.cpp
data/data_document.h data/data_document.h
data/data_document_media.cpp data/data_document_media.cpp
@ -415,6 +425,8 @@ PRIVATE
data/data_notify_settings.h data/data_notify_settings.h
data/data_peer.cpp data/data_peer.cpp
data/data_peer.h data/data_peer.h
data/data_peer_id.cpp
data/data_peer_id.h
data/data_peer_values.cpp data/data_peer_values.cpp
data/data_peer_values.h data/data_peer_values.h
data/data_photo.cpp data/data_photo.cpp
@ -811,8 +823,6 @@ PRIVATE
passport/passport_panel.h passport/passport_panel.h
passport/passport_panel_controller.cpp passport/passport_panel_controller.cpp
passport/passport_panel_controller.h passport/passport_panel_controller.h
passport/passport_panel_details_row.cpp
passport/passport_panel_details_row.h
passport/passport_panel_edit_contact.cpp passport/passport_panel_edit_contact.cpp
passport/passport_panel_edit_contact.h passport/passport_panel_edit_contact.h
passport/passport_panel_edit_document.cpp passport/passport_panel_edit_document.cpp
@ -823,6 +833,10 @@ PRIVATE
passport/passport_panel_form.h passport/passport_panel_form.h
passport/passport_panel_password.cpp passport/passport_panel_password.cpp
passport/passport_panel_password.h passport/passport_panel_password.h
payments/payments_checkout_process.cpp
payments/payments_checkout_process.h
payments/payments_form.cpp
payments/payments_form.h
platform/linux/linux_desktop_environment.cpp platform/linux/linux_desktop_environment.cpp
platform/linux/linux_desktop_environment.h platform/linux/linux_desktop_environment.h
platform/linux/linux_gdk_helper.cpp platform/linux/linux_gdk_helper.cpp
@ -1022,8 +1036,6 @@ PRIVATE
ui/widgets/level_meter.h ui/widgets/level_meter.h
ui/widgets/multi_select.cpp ui/widgets/multi_select.cpp
ui/widgets/multi_select.h ui/widgets/multi_select.h
ui/widgets/separate_panel.cpp
ui/widgets/separate_panel.h
ui/countryinput.cpp ui/countryinput.cpp
ui/countryinput.h ui/countryinput.h
ui/empty_userpic.cpp ui/empty_userpic.cpp
@ -1039,8 +1051,6 @@ PRIVATE
ui/search_field_controller.h ui/search_field_controller.h
ui/special_buttons.cpp ui/special_buttons.cpp
ui/special_buttons.h ui/special_buttons.h
ui/special_fields.cpp
ui/special_fields.h
ui/unread_badge.cpp ui/unread_badge.cpp
ui/unread_badge.h ui/unread_badge.h
window/main_window.cpp window/main_window.cpp

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 B

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 B

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 B

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,022 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -216,6 +216,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_code_desc" = "We have sent you a message with activation\ncode to your phone. Please enter it below."; "lng_code_desc" = "We have sent you a message with activation\ncode to your phone. Please enter it below.";
"lng_code_from_telegram" = "Please enter the code you've just received\nin your previous **Telegram** app."; "lng_code_from_telegram" = "Please enter the code you've just received\nin your previous **Telegram** app.";
"lng_code_no_telegram" = "Send code via SMS"; "lng_code_no_telegram" = "Send code via SMS";
"lng_code_register_phone" = "If you already signed up for Telegram, please enter the code which was sent to your mobile app.\n\nIf you haven’t signed up yet, please register from your phone or tablet first.";
"lng_code_call" = "Telegram will call you in {minutes}:{seconds}"; "lng_code_call" = "Telegram will call you in {minutes}:{seconds}";
"lng_code_calling" = "Requesting a call from Telegram..."; "lng_code_calling" = "Requesting a call from Telegram...";
"lng_code_called" = "Telegram dialed your number"; "lng_code_called" = "Telegram dialed your number";
@ -453,6 +454,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_sensitive_title" = "Sensitive content"; "lng_settings_sensitive_title" = "Sensitive content";
"lng_settings_sensitive_disable_filtering" = "Disable filtering"; "lng_settings_sensitive_disable_filtering" = "Disable filtering";
"lng_settings_sensitive_about" = "Display sensitive media in public channels on all your Telegram devices."; "lng_settings_sensitive_about" = "Display sensitive media in public channels on all your Telegram devices.";
"lng_settings_security_bots" = "Bots and websites";
"lng_settings_clear_payment_info" = "Clear Payment and Shipping Info";
"lng_clear_payment_info_title" = "Clear payment info";
"lng_clear_payment_info_sure" = "Are you sure you want to clear your payment and shipping info?";
"lng_clear_payment_info_shipping" = "Shipping info";
"lng_clear_payment_info_payment" = "Payment info";
"lng_clear_payment_info_clear" = "Clear";
"lng_clear_payment_info_confirm" = "Delete your shipping info and instruct all payment providers to remove your saved credit cards? Note that Telegram never stores your credit card data.";
"lng_settings_auto_night_mode" = "Auto-Night mode"; "lng_settings_auto_night_mode" = "Auto-Night mode";
"lng_settings_auto_night_enabled" = "Match the system settings"; "lng_settings_auto_night_enabled" = "Match the system settings";
@ -1097,8 +1107,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_invite_user_chat" = "the voice chat"; "lng_action_invite_user_chat" = "the voice chat";
"lng_action_invite_users_and_one" = "{accumulated}, {user}"; "lng_action_invite_users_and_one" = "{accumulated}, {user}";
"lng_action_invite_users_and_last" = "{accumulated} and {user}"; "lng_action_invite_users_and_last" = "{accumulated} and {user}";
"lng_action_group_call_started" = "{from} started {chat}"; "lng_action_group_call_started_group" = "{from} started a voice chat";
"lng_action_group_call_started_chat" = "a voice chat"; "lng_action_group_call_started_channel" = "Voice chat started";
"lng_action_group_call_scheduled_group" = "{from} scheduled a voice chat for {date}";
"lng_action_group_call_scheduled_channel" = "Voice chat scheduled for {date}";
"lng_action_group_call_finished" = "Voice chat finished ({duration})"; "lng_action_group_call_finished" = "Voice chat finished ({duration})";
"lng_action_add_user" = "{from} added {user}"; "lng_action_add_user" = "{from} added {user}";
"lng_action_add_users_many" = "{from} added {users}"; "lng_action_add_users_many" = "{from} added {users}";
@ -1856,11 +1868,66 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_theme_editor_menu_show" = "Show palette file"; "lng_theme_editor_menu_show" = "Show palette file";
"lng_payments_not_supported" = "Sorry, Telegram Desktop doesn't support payments yet. Please use one of our mobile apps to do this."; "lng_payments_not_supported" = "Sorry, Telegram Desktop doesn't support payments yet. Please use one of our mobile apps to do this.";
"lng_payments_webview_no_card" = "Unfortunately, you can't add a new card with current system configuration.";
"lng_payments_webview_no_use" = "Unfortunately, you can't use payments with current system configuration.";
"lng_payments_webview_install_edge" = "Please install {link}.";
"lng_payments_webview_install_webkit" = "Please install WebKitGTK 4 (webkit2gtk-4.0) using your package manager.";
"lng_payments_webview_switch_mutter" = "Qt's window embedding doesn't work well with Mutter window manager. Please switch to another window manager or desktop environment.";
"lng_payments_webview_switch_wayland" = "There is no way to embed WebView window on Wayland. Please switch to X11.";
"lng_payments_sure_close" = "Are you sure you want to close this payment form? The changes you made will be lost.";
"lng_payments_receipt_label" = "Receipt"; "lng_payments_receipt_label" = "Receipt";
"lng_payments_receipt_label_test" = "Test receipt"; "lng_payments_receipt_label_test" = "Test receipt";
"lng_payments_invoice_label" = "Invoice"; "lng_payments_invoice_label" = "Invoice";
"lng_payments_invoice_label_test" = "Test invoice"; "lng_payments_invoice_label_test" = "Test invoice";
"lng_payments_receipt_button" = "Receipt"; "lng_payments_receipt_button" = "Receipt";
"lng_payments_success" = "You paid {amount} for {title}.";
"lng_payments_checkout_title" = "Checkout";
"lng_payments_receipt_title" = "Receipt";
"lng_payments_total_label" = "Total";
"lng_payments_date_label" = "Paid";
"lng_payments_pay_amount" = "Pay {amount}";
"lng_payments_payment_method" = "Payment Method";
"lng_payments_new_card" = "New Card...";
"lng_payments_shipping_address" = "Shipping Address";
"lng_payments_address_street1" = "Address 1";
"lng_payments_address_street2" = "Address 2";
"lng_payments_address_city" = "City";
"lng_payments_address_state" = "State";
"lng_payments_address_country" = "Country";
"lng_payments_address_postcode" = "Postcode";
"lng_payments_shipping_method" = "Shipping Method";
"lng_payments_info_name" = "Name";
"lng_payments_info_email" = "Email";
"lng_payments_info_phone" = "Phone";
"lng_payments_to_provider_phone_email" = "Phone and Email will be passed to {provider} as billing info.";
"lng_payments_to_provider_email" = "Email will be passed to {provider} as billing info.";
"lng_payments_to_provider_phone" = "Phone will be passed to {provider} as billing info.";
"lng_payments_processed_by" = "Processed by {provider}";
"lng_payments_warning_title" = "Warning";
"lng_payments_warning_body" = "Neither Telegram, nor {bot1} will have access to your credit card information. Credit card details will be handled only by the payment system, {provider}.\n\nPayments will go directly to the developer of {bot2}. Telegram cannot provide any guarantees, so proceed at your own risk. In case of problems, please contact the developer of {bot3} or your bank.";
"lng_payments_shipping_address_title" = "Shipping Information";
"lng_payments_card_title" = "New Card";
"lng_payments_card_number" = "Card Number";
"lng_payments_card_holder" = "Cardholder name";
"lng_payments_billing_address" = "Billing Information";
"lng_payments_billing_country" = "Country";
"lng_payments_billing_zip_code" = "Zip Code";
"lng_payments_save_information" = "Save Information for future use";
"lng_payments_need_password" = "You can save your payment information for future use. Please turn on Two-Step Verification to enable this.";
"lng_payments_password_title" = "Payment Confirmation";
"lng_payments_password_description" = "Your card {card} is on file. To pay with this card, please enter your 2-Step-Verification password.";
"lng_payments_password_submit" = "Pay";
"lng_payments_tips_label" = "Tip (Optional)";
"lng_payments_tips_box_title" = "Add Tip";
"lng_payments_tips_max" = "Max possible tip amount: {amount}";
"lng_payments_shipping_not_available" = "Shipping to the selected country is not available.";
"lng_payments_card_declined" = "Your card was declined.";
"lng_payments_payment_failed" = "Payment failed. Your card has not been billed.";
"lng_payments_precheckout_failed" = "The bot couldn't process your payment. Your card has not been billed.";
"lng_payments_already_paid" = "You have already paid for this item.";
"lng_call_status_incoming" = "is calling you..."; "lng_call_status_incoming" = "is calling you...";
"lng_call_status_connecting" = "connecting..."; "lng_call_status_connecting" = "connecting...";
@ -1927,6 +1994,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_inactive" = "listening"; "lng_group_call_inactive" = "listening";
"lng_group_call_raised_hand_status" = "wants to speak"; "lng_group_call_raised_hand_status" = "wants to speak";
"lng_group_call_settings" = "Settings"; "lng_group_call_settings" = "Settings";
"lng_group_call_share_button" = "Share";
"lng_group_call_unmute" = "Unmute"; "lng_group_call_unmute" = "Unmute";
"lng_group_call_unmute_sub" = "or hold spacebar to talk"; "lng_group_call_unmute_sub" = "or hold spacebar to talk";
"lng_group_call_you_are_live" = "You are Live"; "lng_group_call_you_are_live" = "You are Live";
@ -1939,6 +2007,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_leave" = "Leave"; "lng_group_call_leave" = "Leave";
"lng_group_call_leave_title" = "Leave voice chat"; "lng_group_call_leave_title" = "Leave voice chat";
"lng_group_call_leave_sure" = "Are you sure you want to leave this voice chat?"; "lng_group_call_leave_sure" = "Are you sure you want to leave this voice chat?";
"lng_group_call_close" = "Close";
"lng_group_call_close_sure" = "Voice chat is scheduled. You can abort it or just close this panel.";
"lng_group_call_also_cancel" = "Abort voice chat";
"lng_group_call_leave_to_other_sure" = "Do you want to leave your active voice chat and join a voice chat in this group?"; "lng_group_call_leave_to_other_sure" = "Do you want to leave your active voice chat and join a voice chat in this group?";
"lng_group_call_create_sure" = "Do you really want to start a voice chat in this group?"; "lng_group_call_create_sure" = "Do you really want to start a voice chat in this group?";
"lng_group_call_create_sure_channel" = "Are you sure you want to start a voice chat in this channel as your personal account?"; "lng_group_call_create_sure_channel" = "Are you sure you want to start a voice chat in this channel as your personal account?";
@ -1969,6 +2040,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_copy_speaker_link" = "Copy Speaker Link"; "lng_group_call_copy_speaker_link" = "Copy Speaker Link";
"lng_group_call_copy_listener_link" = "Copy Listener Link"; "lng_group_call_copy_listener_link" = "Copy Listener Link";
"lng_group_call_end" = "End Voice Chat"; "lng_group_call_end" = "End Voice Chat";
"lng_group_call_cancel" = "Abort Voice Chat";
"lng_group_call_join" = "Join"; "lng_group_call_join" = "Join";
"lng_group_call_join_confirm" = "Do you want to join the voice chat {chat}?"; "lng_group_call_join_confirm" = "Do you want to join the voice chat {chat}?";
"lng_group_call_invite_done_user" = "You invited {user} to the voice chat."; "lng_group_call_invite_done_user" = "You invited {user} to the voice chat.";
@ -2003,6 +2075,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_join_as_header" = "Join Voice Chat as..."; "lng_group_call_join_as_header" = "Join Voice Chat as...";
"lng_group_call_display_as_header" = "Display me as..."; "lng_group_call_display_as_header" = "Display me as...";
"lng_group_call_join_as_about" = "Choose whether you want to be displayed as your personal account or as your channel."; "lng_group_call_join_as_about" = "Choose whether you want to be displayed as your personal account or as your channel.";
"lng_group_call_or_schedule" = "You can also {link}.";
"lng_group_call_schedule" = "schedule a voice chat";
"lng_group_call_schedule_title" = "Schedule Voice Chat";
"lng_group_call_schedule_notified_group" = "The members of the group will be notified that the voice chat will start in {duration}.";
"lng_group_call_schedule_notified_channel" = "The subscribers of the channel will be notified that the voice chat will start in {duration}.";
"lng_group_call_scheduled_status" = "Scheduled";
"lng_group_call_scheduled_title" = "Scheduled Voice Chat";
"lng_group_call_starts_short" = "Starts {when}";
"lng_group_call_starts" = "Voice Chat starts {when}";
"lng_group_call_starts_today" = "today at {time}";
"lng_group_call_starts_tomorrow" = "tomorrow at {time}";
"lng_group_call_starts_date" = "{date} at {time}";
"lng_group_call_starts_in" = "Starts in";
"lng_group_call_late_by" = "Late by";
"lng_group_call_starts_short_today" = "Today, {time}";
"lng_group_call_starts_short_tomorrow" = "Tomorrow, {time}";
"lng_group_call_starts_short_date" = "{date}, {time}";
"lng_group_call_start_now" = "Start Now";
"lng_group_call_start_now_sure" = "Are you sure you want to start the voice chat now?";
"lng_group_call_set_reminder" = "Set Reminder";
"lng_group_call_cancel_reminder" = "Cancel Reminder";
"lng_group_call_join_as_personal" = "personal account"; "lng_group_call_join_as_personal" = "personal account";
"lng_group_call_edit_title" = "Edit voice chat title"; "lng_group_call_edit_title" = "Edit voice chat title";
"lng_group_call_switch_done" = "Members of this voice chat will now see you as **{user}**"; "lng_group_call_switch_done" = "Members of this voice chat will now see you as **{user}**";

View file

@ -69,9 +69,8 @@
<file alias="day-blue.tdesktop-theme">../../day-blue.tdesktop-theme</file> <file alias="day-blue.tdesktop-theme">../../day-blue.tdesktop-theme</file>
<file alias="night.tdesktop-theme">../../night.tdesktop-theme</file> <file alias="night.tdesktop-theme">../../night.tdesktop-theme</file>
<file alias="night-green.tdesktop-theme">../../night-green.tdesktop-theme</file> <file alias="night-green.tdesktop-theme">../../night-green.tdesktop-theme</file>
<file alias="icons/calls/active_hand.json">../../icons/calls/active_hand.json</file> <file alias="icons/calls/hands.lottie">../../icons/calls/hands.lottie</file>
<file alias="icons/calls/hand_muted_active.json">../../icons/calls/hand_muted_active.json</file> <file alias="icons/calls/voice.lottie">../../icons/calls/voice.lottie</file>
<file alias="icons/calls/raised_hand.json">../../icons/calls/raised_hand.json</file>
</qresource> </qresource>
<qresource prefix="/qt-project.org"> <qresource prefix="/qt-project.org">
<file>../qmime/freedesktop.org.xml</file> <file>../qmime/freedesktop.org.xml</file>

View file

@ -68,7 +68,7 @@ inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string pro
inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia; inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia;
inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia; inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia;
inputMediaGame#d33f43f3 id:InputGame = InputMedia; inputMediaGame#d33f43f3 id:InputGame = InputMedia;
inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia; inputMediaInvoice#f4e096c3 flags:# multiple_allowed:flags.1?true can_forward:flags.2?true title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia; inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia;
inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia; inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia;
inputMediaDice#e66fbf7b emoticon:string = InputMedia; inputMediaDice#e66fbf7b emoticon:string = InputMedia;
@ -90,8 +90,8 @@ inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;
inputTakeoutFileLocation#29be5899 = InputFileLocation; inputTakeoutFileLocation#29be5899 = InputFileLocation;
inputPhotoFileLocation#40181ffe id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation; inputPhotoFileLocation#40181ffe id:long access_hash:long file_reference:bytes thumb_size:string = InputFileLocation;
inputPhotoLegacyFileLocation#d83466f3 id:long access_hash:long file_reference:bytes volume_id:long local_id:int secret:long = InputFileLocation; inputPhotoLegacyFileLocation#d83466f3 id:long access_hash:long file_reference:bytes volume_id:long local_id:int secret:long = InputFileLocation;
inputPeerPhotoFileLocation#27d69997 flags:# big:flags.0?true peer:InputPeer volume_id:long local_id:int = InputFileLocation; inputPeerPhotoFileLocation#37257e99 flags:# big:flags.0?true peer:InputPeer photo_id:long = InputFileLocation;
inputStickerSetThumb#dbaeae9 stickerset:InputStickerSet volume_id:long local_id:int = InputFileLocation; inputStickerSetThumb#9d84f3db stickerset:InputStickerSet thumb_version:int = InputFileLocation;
inputGroupCallStream#bba51639 call:InputGroupCall time_ms:long scale:int = InputFileLocation; inputGroupCallStream#bba51639 call:InputGroupCall time_ms:long scale:int = InputFileLocation;
peerUser#9db1bc6d user_id:int = Peer; peerUser#9db1bc6d user_id:int = Peer;
@ -113,7 +113,7 @@ userEmpty#200250ba id:int = User;
user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User; user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#69d3ab26 flags:# has_video:flags.0?true photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto; userProfilePhoto#82d1f706 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
userStatusEmpty#9d05049 = UserStatus; userStatusEmpty#9d05049 = UserStatus;
userStatusOnline#edb93949 expires:int = UserStatus; userStatusOnline#edb93949 expires:int = UserStatus;
@ -139,7 +139,7 @@ chatParticipantsForbidden#fc900c2b flags:# chat_id:int self_participant:flags.0?
chatParticipants#3f460fed chat_id:int participants:Vector<ChatParticipant> version:int = ChatParticipants; chatParticipants#3f460fed chat_id:int participants:Vector<ChatParticipant> version:int = ChatParticipants;
chatPhotoEmpty#37c1011c = ChatPhoto; chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#d20b9f3c flags:# has_video:flags.0?true photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto; chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message; messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
message#bce383d2 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message; message#bce383d2 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message;
@ -186,6 +186,7 @@ messageActionGeoProximityReached#98e0d697 from_id:Peer to_id:Peer distance:int =
messageActionGroupCall#7a0d7f42 flags:# call:InputGroupCall duration:flags.0?int = MessageAction; messageActionGroupCall#7a0d7f42 flags:# call:InputGroupCall duration:flags.0?int = MessageAction;
messageActionInviteToGroupCall#76b9f11a call:InputGroupCall users:Vector<int> = MessageAction; messageActionInviteToGroupCall#76b9f11a call:InputGroupCall users:Vector<int> = MessageAction;
messageActionSetMessagesTTL#aa1afbfd period:int = MessageAction; messageActionSetMessagesTTL#aa1afbfd period:int = MessageAction;
messageActionGroupCallScheduled#b3a07661 call:InputGroupCall schedule_date:int = MessageAction;
dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog; dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog; dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
@ -194,10 +195,10 @@ photoEmpty#2331b22d id:long = Photo;
photo#fb197a65 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> video_sizes:flags.1?Vector<VideoSize> dc_id:int = Photo; photo#fb197a65 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> video_sizes:flags.1?Vector<VideoSize> dc_id:int = Photo;
photoSizeEmpty#e17e23c type:string = PhotoSize; photoSizeEmpty#e17e23c type:string = PhotoSize;
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize; photoSize#75c78e60 type:string w:int h:int size:int = PhotoSize;
photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize; photoCachedSize#21e1ad6 type:string w:int h:int bytes:bytes = PhotoSize;
photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize; photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize;
photoSizeProgressive#5aa86a51 type:string location:FileLocation w:int h:int sizes:Vector<int> = PhotoSize; photoSizeProgressive#fa3efb95 type:string w:int h:int sizes:Vector<int> = PhotoSize;
photoPathSize#d8214d41 type:string bytes:bytes = PhotoSize; photoPathSize#d8214d41 type:string bytes:bytes = PhotoSize;
geoPointEmpty#1117dd5f = GeoPoint; geoPointEmpty#1117dd5f = GeoPoint;
@ -554,7 +555,7 @@ inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet; inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
inputStickerSetDice#e67f520e emoticon:string = InputStickerSet; inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
stickerSet#40e237a8 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int count:int hash:int = StickerSet; stickerSet#d7df217a flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int count:int hash:int = StickerSet;
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet; messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
@ -648,6 +649,7 @@ inputBotInlineMessageMediaGeo#96929a85 flags:# geo_point:InputGeoPoint heading:f
inputBotInlineMessageMediaVenue#417bbf11 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; inputBotInlineMessageMediaVenue#417bbf11 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaContact#a6edbffd flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; inputBotInlineMessageMediaContact#a6edbffd flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage; inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaInvoice#d5348d85 flags:# multiple_allowed:flags.1?true can_forward:flags.3?true title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult; inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult; inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult;
@ -659,6 +661,7 @@ botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string ent
botInlineMessageMediaGeo#51846fd flags:# geo:GeoPoint heading:flags.0?int period:flags.1?int proximity_notification_radius:flags.3?int reply_markup:flags.2?ReplyMarkup = BotInlineMessage; botInlineMessageMediaGeo#51846fd flags:# geo:GeoPoint heading:flags.0?int period:flags.1?int proximity_notification_radius:flags.3?int reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaVenue#8a86659c flags:# geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage; botInlineMessageMediaVenue#8a86659c flags:# geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaContact#18d1cdc2 flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage; botInlineMessageMediaContact#18d1cdc2 flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaInvoice#354a9b09 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument currency:string total_amount:long reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult; botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult;
botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult; botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult;
@ -792,7 +795,7 @@ dataJSON#7d748d04 data:string = DataJSON;
labeledPrice#cb296bf8 label:string amount:long = LabeledPrice; labeledPrice#cb296bf8 label:string amount:long = LabeledPrice;
invoice#c30aa358 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true currency:string prices:Vector<LabeledPrice> = Invoice; invoice#cd886e0 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true currency:string prices:Vector<LabeledPrice> max_tip_amount:flags.8?long suggested_tip_amounts:flags.8?Vector<long> = Invoice;
paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge; paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge;
@ -812,14 +815,14 @@ inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w
upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile; upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;
payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true bot_id:int invoice:Invoice provider_id:int url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector<User> = payments.PaymentForm; payments.paymentForm#8d0b2415 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true form_id:long bot_id:int invoice:Invoice provider_id:int url:string native_provider:flags.4?string native_params:flags.4?DataJSON saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?PaymentSavedCredentials users:Vector<User> = payments.PaymentForm;
payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = payments.ValidatedRequestedInfo; payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = payments.ValidatedRequestedInfo;
payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult; payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult;
payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult; payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult;
payments.paymentReceipt#500911e1 flags:# date:int bot_id:int invoice:Invoice provider_id:int info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption currency:string total_amount:long credentials_title:string users:Vector<User> = payments.PaymentReceipt; payments.paymentReceipt#10b555d0 flags:# date:int bot_id:int provider_id:int title:string description:string photo:flags.2?WebDocument invoice:Invoice info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption tip_amount:flags.3?long currency:string total_amount:long credentials_title:string users:Vector<User> = payments.PaymentReceipt;
payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_info:flags.0?PaymentRequestedInfo = payments.SavedInfo; payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_info:flags.0?PaymentRequestedInfo = payments.SavedInfo;
@ -1088,8 +1091,6 @@ emojiURL#a575739d url:string = EmojiURL;
emojiLanguage#b3fb5361 lang_code:string = EmojiLanguage; emojiLanguage#b3fb5361 lang_code:string = EmojiLanguage;
fileLocationToBeDeprecated#bc7fc6cd volume_id:long local_id:int = FileLocation;
folder#ff544e65 flags:# autofill_new_broadcasts:flags.0?true autofill_public_groups:flags.1?true autofill_new_correspondents:flags.2?true id:int title:string photo:flags.3?ChatPhoto = Folder; folder#ff544e65 flags:# autofill_new_broadcasts:flags.0?true autofill_public_groups:flags.1?true autofill_new_correspondents:flags.2?true id:int title:string photo:flags.3?ChatPhoto = Folder;
inputFolderPeer#fbd2c296 peer:InputPeer folder_id:int = InputFolderPeer; inputFolderPeer#fbd2c296 peer:InputPeer folder_id:int = InputFolderPeer;
@ -1169,7 +1170,7 @@ stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueA
help.promoDataEmpty#98f6ac75 expires:int = help.PromoData; help.promoDataEmpty#98f6ac75 expires:int = help.PromoData;
help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer chats:Vector<Chat> users:Vector<User> psa_type:flags.1?string psa_message:flags.2?string = help.PromoData; help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer chats:Vector<Chat> users:Vector<User> psa_type:flags.1?string psa_message:flags.2?string = help.PromoData;
videoSize#e831c556 flags:# type:string location:FileLocation w:int h:int size:int video_start_ts:flags.0?double = VideoSize; videoSize#de33b094 flags:# type:string w:int h:int size:int video_start_ts:flags.0?double = VideoSize;
statsGroupTopPoster#18f3d0f7 user_id:int messages:int avg_chars:int = StatsGroupTopPoster; statsGroupTopPoster#18f3d0f7 user_id:int messages:int avg_chars:int = StatsGroupTopPoster;
@ -1203,11 +1204,11 @@ peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;
stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats; stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats;
groupCallDiscarded#7780bcb4 id:long access_hash:long duration:int = GroupCall; groupCallDiscarded#7780bcb4 id:long access_hash:long duration:int = GroupCall;
groupCall#c0c2052e flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true id:long access_hash:long participants_count:int params:flags.0?DataJSON title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int version:int = GroupCall; groupCall#c95c6654 flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true id:long access_hash:long participants_count:int params:flags.0?DataJSON title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int version:int = GroupCall;
inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall; inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall;
groupCallParticipant#19adba89 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true self:flags.12?true peer:Peer date:int active_date:flags.3?int source:int volume:flags.7?int about:flags.11?string raise_hand_rating:flags.13?long = GroupCallParticipant; groupCallParticipant#b96b25ee flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true self:flags.12?true peer:Peer date:int active_date:flags.3?int source:int volume:flags.7?int about:flags.11?string raise_hand_rating:flags.13?long params:flags.6?DataJSON = GroupCallParticipant;
phone.groupCall#9e727aad call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string chats:Vector<Chat> users:Vector<User> = phone.GroupCall; phone.groupCall#9e727aad call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string chats:Vector<Chat> users:Vector<User> = phone.GroupCall;
@ -1592,10 +1593,10 @@ bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
bots.setBotCommands#805d46f6 commands:Vector<BotCommand> = Bool; bots.setBotCommands#805d46f6 commands:Vector<BotCommand> = Bool;
payments.getPaymentForm#99f09745 msg_id:int = payments.PaymentForm; payments.getPaymentForm#8a333c8d flags:# peer:InputPeer msg_id:int theme_params:flags.0?DataJSON = payments.PaymentForm;
payments.getPaymentReceipt#a092a980 msg_id:int = payments.PaymentReceipt; payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt;
payments.validateRequestedInfo#770a8e74 flags:# save:flags.0?true msg_id:int info:PaymentRequestedInfo = payments.ValidatedRequestedInfo; payments.validateRequestedInfo#db103170 flags:# save:flags.0?true peer:InputPeer msg_id:int info:PaymentRequestedInfo = payments.ValidatedRequestedInfo;
payments.sendPaymentForm#2b8879b3 flags:# msg_id:int requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials = payments.PaymentResult; payments.sendPaymentForm#30c3bc9d flags:# form_id:long peer:InputPeer msg_id:int requested_info_id:flags.0?string shipping_option_id:flags.1?string credentials:InputPaymentCredentials tip_amount:flags.2?long = payments.PaymentResult;
payments.getSavedInfo#227d824b = payments.SavedInfo; payments.getSavedInfo#227d824b = payments.SavedInfo;
payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool; payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool;
payments.getBankCardData#2e79d779 number:string = payments.BankCardData; payments.getBankCardData#2e79d779 number:string = payments.BankCardData;
@ -1615,7 +1616,7 @@ phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall durati
phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates; phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates;
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool; phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool;
phone.createGroupCall#bd3dabe0 peer:InputPeer random_id:int = Updates; phone.createGroupCall#48cdc6d8 flags:# peer:InputPeer random_id:int title:flags.0?string schedule_date:flags.1?int = Updates;
phone.joinGroupCall#b132ff7b flags:# muted:flags.0?true call:InputGroupCall join_as:InputPeer invite_hash:flags.1?string params:DataJSON = Updates; phone.joinGroupCall#b132ff7b flags:# muted:flags.0?true call:InputGroupCall join_as:InputPeer invite_hash:flags.1?string params:DataJSON = Updates;
phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates; phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates;
phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector<InputUser> = Updates; phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector<InputUser> = Updates;
@ -1629,6 +1630,9 @@ phone.editGroupCallParticipant#d975eb80 flags:# muted:flags.0?true call:InputGro
phone.editGroupCallTitle#1ca6ac0a call:InputGroupCall title:string = Updates; phone.editGroupCallTitle#1ca6ac0a call:InputGroupCall title:string = Updates;
phone.getGroupCallJoinAs#ef7c213a peer:InputPeer = phone.JoinAsPeers; phone.getGroupCallJoinAs#ef7c213a peer:InputPeer = phone.JoinAsPeers;
phone.exportGroupCallInvite#e6aa647f flags:# can_self_unmute:flags.0?true call:InputGroupCall = phone.ExportedGroupCallInvite; phone.exportGroupCallInvite#e6aa647f flags:# can_self_unmute:flags.0?true call:InputGroupCall = phone.ExportedGroupCallInvite;
phone.toggleGroupCallStartSubscription#219c34e6 call:InputGroupCall subscribed:Bool = Updates;
phone.startScheduledGroupCall#5680e342 call:InputGroupCall = Updates;
phone.saveDefaultGroupCallJoinAs#575e1f8c peer:InputPeer join_as:InputPeer = Bool;
langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference; langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference;
langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>; langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>;
@ -1645,4 +1649,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats; stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
// LAYER 126 // LAYER 128

View file

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

View file

@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,7,1,0 FILEVERSION 2,7,2,0
PRODUCTVERSION 2,7,1,0 PRODUCTVERSION 2,7,2,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -62,10 +62,10 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Telegram FZ-LLC" VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop" VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "2.7.1.0" VALUE "FileVersion", "2.7.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop" VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.7.1.0" VALUE "ProductVersion", "2.7.2.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

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

View file

@ -54,7 +54,7 @@ JoinedByLinkSlice ParseJoinedByLinkSlice(
for (const auto &importer : data.vimporters().v) { for (const auto &importer : data.vimporters().v) {
importer.match([&](const MTPDchatInviteImporter &data) { importer.match([&](const MTPDchatInviteImporter &data) {
result.users.push_back({ result.users.push_back({
.user = owner.user(data.vuser_id().v), .user = owner.user(data.vuser_id()),
.date = data.vdate().v, .date = data.vdate().v,
}); });
}); });
@ -623,7 +623,7 @@ auto InviteLinks::parse(
return invite.match([&](const MTPDchatInviteExported &data) { return invite.match([&](const MTPDchatInviteExported &data) {
return Link{ return Link{
.link = qs(data.vlink()), .link = qs(data.vlink()),
.admin = peer->session().data().user(data.vadmin_id().v), .admin = peer->session().data().user(data.vadmin_id()),
.date = data.vdate().v, .date = data.vdate().v,
.startDate = data.vstart_date().value_or_empty(), .startDate = data.vstart_date().value_or_empty(),
.expireDate = data.vexpire_date().value_or_empty(), .expireDate = data.vexpire_date().value_or_empty(),

View file

@ -37,7 +37,7 @@ Key ExtractKey(const QString &query) {
const auto params = parse(); const auto params = parse();
const auto channel = params.value("channel"); const auto channel = params.value("channel");
const auto post = params.value("post").toInt(); const auto post = params.value("post").toInt();
return (channel.toInt() && post) ? Key{ channel, post } : Key(); return (channel.toULongLong() && post) ? Key{ channel, post } : Key();
} else if (check.startsWith(qstr("tg://resolve"), Qt::CaseInsensitive)) { } else if (check.startsWith(qstr("tg://resolve"), Qt::CaseInsensitive)) {
const auto params = parse(); const auto params = parse();
const auto domain = params.value("domain"); const auto domain = params.value("domain");
@ -112,7 +112,7 @@ std::optional<HistoryItem*> SingleMessageSearch::performLookupByChannel(
&& received.messageIds.front() == postId) { && received.messageIds.front() == postId) {
_cache.emplace( _cache.emplace(
_requestKey, _requestKey,
FullMsgId(channel->bareId(), postId)); FullMsgId(peerToChannel(channel->id), postId));
ready(); ready();
} else { } else {
fail(); fail();
@ -142,7 +142,7 @@ std::optional<HistoryItem*> SingleMessageSearch::performLookupById(
_requestId = _session->api().request(MTPchannels_GetChannels( _requestId = _session->api().request(MTPchannels_GetChannels(
MTP_vector<MTPInputChannel>( MTP_vector<MTPInputChannel>(
1, 1,
MTP_inputChannel(MTP_int(channelId), MTP_long(0))) MTP_inputChannel(MTP_int(channelId.bare), MTP_long(0))) // #TODO ids
)).done([=](const MTPmessages_Chats &result) { )).done([=](const MTPmessages_Chats &result) {
result.match([&](const auto &data) { result.match([&](const auto &data) {
const auto peer = _session->data().processChats(data.vchats()); const auto peer = _session->data().processChats(data.vchats());
@ -212,7 +212,7 @@ std::optional<HistoryItem*> SingleMessageSearch::performLookup(
if (!_requestKey.domainOrId[0].isDigit()) { if (!_requestKey.domainOrId[0].isDigit()) {
return performLookupByUsername(_requestKey.domainOrId, ready); return performLookupByUsername(_requestKey.domainOrId, ready);
} }
const auto channelId = _requestKey.domainOrId.toInt(); const auto channelId = ChannelId(_requestKey.domainOrId.toULongLong());
return performLookupById(channelId, ready); return performLookupById(channelId, ready);
} }

View file

@ -37,15 +37,17 @@ EntitiesInText EntitiesFromMTP(
case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break; case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityMentionName: { case mtpc_messageEntityMentionName: {
const auto &d = entity.c_messageEntityMentionName(); const auto &d = entity.c_messageEntityMentionName();
const auto userId = UserId(d.vuser_id());
const auto data = [&] { const auto data = [&] {
if (session) { if (session) {
if (const auto user = session->data().userLoaded(d.vuser_id().v)) { if (const auto user = session->data().userLoaded(userId)) {
return MentionNameDataFromFields({ return MentionNameDataFromFields({
d.vuser_id().v, userId.bare,
user->accessHash() }); user->accessHash()
});
} }
} }
return MentionNameDataFromFields(d.vuser_id().v); return MentionNameDataFromFields(userId.bare);
}(); }();
result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data }); result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data });
} break; } break;
@ -53,10 +55,11 @@ EntitiesInText EntitiesFromMTP(
const auto &d = entity.c_inputMessageEntityMentionName(); const auto &d = entity.c_inputMessageEntityMentionName();
const auto data = [&] { const auto data = [&] {
if (session && d.vuser_id().type() == mtpc_inputUserSelf) { if (session && d.vuser_id().type() == mtpc_inputUserSelf) {
return MentionNameDataFromFields(session->userId()); return MentionNameDataFromFields(session->userId().bare);
} else if (d.vuser_id().type() == mtpc_inputUser) { } else if (d.vuser_id().type() == mtpc_inputUser) {
auto &user = d.vuser_id().c_inputUser(); auto &user = d.vuser_id().c_inputUser();
return MentionNameDataFromFields({ user.vuser_id().v, user.vaccess_hash().v }); const auto userId = UserId(user.vuser_id());
return MentionNameDataFromFields({ userId.bare, user.vaccess_hash().v });
} }
return QString(); return QString();
}(); }();
@ -130,7 +133,7 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
if (uid == session->userId()) { if (uid == session->userId()) {
return MTP_inputUserSelf(); return MTP_inputUserSelf();
} else if (const auto user = session->data().userLoaded(uid)) { } else if (const auto user = session->data().userLoaded(uid)) {
return MTP_inputUser(MTP_int(uid), MTP_long(user->accessHash())); return MTP_inputUser(MTP_int(uid.bare), MTP_long(user->accessHash()));
} }
} }
return MTP_inputUserEmpty(); return MTP_inputUserEmpty();
@ -148,7 +151,7 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
case EntityType::MentionName: { case EntityType::MentionName: {
auto inputUser = [&](const QString &data) -> MTPInputUser { auto inputUser = [&](const QString &data) -> MTPInputUser {
auto fields = MentionNameDataToFields(data); auto fields = MentionNameDataToFields(data);
if (session && fields.userId == session->userId()) { if (session && fields.userId == session->userId().bare) {
return MTP_inputUserSelf(); return MTP_inputUserSelf();
} else if (fields.userId) { } else if (fields.userId) {
return MTP_inputUser(MTP_int(fields.userId), MTP_long(fields.accessHash)); return MTP_inputUser(MTP_int(fields.userId), MTP_long(fields.accessHash));

View file

@ -131,13 +131,13 @@ bool MentionUsersLoaded(
for (const auto &entity : entities.v) { for (const auto &entity : entities.v) {
auto type = entity.type(); auto type = entity.type();
if (type == mtpc_messageEntityMentionName) { if (type == mtpc_messageEntityMentionName) {
if (!session->data().userLoaded(entity.c_messageEntityMentionName().vuser_id().v)) { if (!session->data().userLoaded(entity.c_messageEntityMentionName().vuser_id())) {
return false; return false;
} }
} else if (type == mtpc_inputMessageEntityMentionName) { } else if (type == mtpc_inputMessageEntityMentionName) {
auto &inputUser = entity.c_inputMessageEntityMentionName().vuser_id(); auto &inputUser = entity.c_inputMessageEntityMentionName().vuser_id();
if (inputUser.type() == mtpc_inputUser) { if (inputUser.type() == mtpc_inputUser) {
if (!session->data().userLoaded(inputUser.c_inputUser().vuser_id().v)) { if (!session->data().userLoaded(inputUser.c_inputUser().vuser_id())) {
return false; return false;
} }
} }
@ -157,7 +157,7 @@ DataIsLoadedResult AllDataLoadedForMessage(
} }
} }
if (const auto viaBotId = message.vvia_bot_id()) { if (const auto viaBotId = message.vvia_bot_id()) {
if (!session->data().userLoaded(viaBotId->v)) { if (!session->data().userLoaded(*viaBotId)) {
return DataIsLoadedResult::NotLoaded; return DataIsLoadedResult::NotLoaded;
} }
} }
@ -181,19 +181,19 @@ DataIsLoadedResult AllDataLoadedForMessage(
} }
return message.vaction().match( return message.vaction().match(
[&](const MTPDmessageActionChatAddUser &action) { [&](const MTPDmessageActionChatAddUser &action) {
for (const MTPint &userId : action.vusers().v) { for (const auto &userId : action.vusers().v) {
if (!session->data().userLoaded(userId.v)) { if (!session->data().userLoaded(userId)) {
return DataIsLoadedResult::NotLoaded; return DataIsLoadedResult::NotLoaded;
} }
} }
return DataIsLoadedResult::Ok; return DataIsLoadedResult::Ok;
}, [&](const MTPDmessageActionChatJoinedByLink &action) { }, [&](const MTPDmessageActionChatJoinedByLink &action) {
if (!session->data().userLoaded(action.vinviter_id().v)) { if (!session->data().userLoaded(action.vinviter_id())) {
return DataIsLoadedResult::NotLoaded; return DataIsLoadedResult::NotLoaded;
} }
return DataIsLoadedResult::Ok; return DataIsLoadedResult::Ok;
}, [&](const MTPDmessageActionChatDeleteUser &action) { }, [&](const MTPDmessageActionChatDeleteUser &action) {
if (!session->data().userLoaded(action.vuser_id().v)) { if (!session->data().userLoaded(action.vuser_id())) {
return DataIsLoadedResult::NotLoaded; return DataIsLoadedResult::NotLoaded;
} }
return DataIsLoadedResult::Ok; return DataIsLoadedResult::Ok;
@ -1020,7 +1020,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
| MTPDmessage::Flag::f_from_id; | MTPDmessage::Flag::f_from_id;
const auto peerUserId = d.is_out() const auto peerUserId = d.is_out()
? d.vuser_id() ? d.vuser_id()
: MTP_int(_session->userId()); : MTP_int(_session->userId().bare); // #TODO ids
_session->data().addNewMessage( _session->data().addNewMessage(
MTP_message( MTP_message(
MTP_flags(flags), MTP_flags(flags),
@ -1309,8 +1309,8 @@ void Updates::applyUpdates(
const auto viaBotId = d.vvia_bot_id(); const auto viaBotId = d.vvia_bot_id();
const auto entities = d.ventities(); const auto entities = d.ventities();
const auto fwd = d.vfwd_from(); const auto fwd = d.vfwd_from();
if (!session().data().userLoaded(d.vuser_id().v) if (!session().data().userLoaded(d.vuser_id())
|| (viaBotId && !session().data().userLoaded(viaBotId->v)) || (viaBotId && !session().data().userLoaded(*viaBotId))
|| (entities && !MentionUsersLoaded(&session(), *entities)) || (entities && !MentionUsersLoaded(&session(), *entities))
|| (fwd && !ForwardedInfoDataLoaded(&session(), *fwd))) { || (fwd && !ForwardedInfoDataLoaded(&session(), *fwd))) {
MTP_LOG(0, ("getDifference " MTP_LOG(0, ("getDifference "
@ -1326,14 +1326,14 @@ void Updates::applyUpdates(
case mtpc_updateShortChatMessage: { case mtpc_updateShortChatMessage: {
auto &d = updates.c_updateShortChatMessage(); auto &d = updates.c_updateShortChatMessage();
const auto noFrom = !session().data().userLoaded(d.vfrom_id().v); const auto noFrom = !session().data().userLoaded(d.vfrom_id());
const auto chat = session().data().chatLoaded(d.vchat_id().v); const auto chat = session().data().chatLoaded(d.vchat_id());
const auto viaBotId = d.vvia_bot_id(); const auto viaBotId = d.vvia_bot_id();
const auto entities = d.ventities(); const auto entities = d.ventities();
const auto fwd = d.vfwd_from(); const auto fwd = d.vfwd_from();
if (!chat if (!chat
|| noFrom || noFrom
|| (viaBotId && !session().data().userLoaded(viaBotId->v)) || (viaBotId && !session().data().userLoaded(*viaBotId))
|| (entities && !MentionUsersLoaded(&session(), *entities)) || (entities && !MentionUsersLoaded(&session(), *entities))
|| (fwd && !ForwardedInfoDataLoaded(&session(), *fwd))) { || (fwd && !ForwardedInfoDataLoaded(&session(), *fwd))) {
MTP_LOG(0, ("getDifference " MTP_LOG(0, ("getDifference "
@ -1491,7 +1491,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateChannelReadMessagesContents: { case mtpc_updateChannelReadMessagesContents: {
auto &d = update.c_updateChannelReadMessagesContents(); auto &d = update.c_updateChannelReadMessagesContents();
auto channel = session().data().channelLoaded(d.vchannel_id().v); auto channel = session().data().channelLoaded(d.vchannel_id());
if (!channel) { if (!channel) {
if (!_byMinChannelTimer.isActive()) { if (!_byMinChannelTimer.isActive()) {
// getDifference after timeout. // getDifference after timeout.
@ -1537,7 +1537,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updatePinnedChannelMessages: { case mtpc_updatePinnedChannelMessages: {
auto &d = update.c_updatePinnedChannelMessages(); auto &d = update.c_updatePinnedChannelMessages();
auto channel = session().data().channelLoaded(d.vchannel_id().v); auto channel = session().data().channelLoaded(d.vchannel_id());
if (channel && !_handlingChannelDifference) { if (channel && !_handlingChannelDifference) {
if (channel->ptsRequesting()) { // skip global updates while getting channel difference if (channel->ptsRequesting()) { // skip global updates while getting channel difference
@ -1623,7 +1623,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateDeleteChannelMessages: { case mtpc_updateDeleteChannelMessages: {
auto &d = update.c_updateDeleteChannelMessages(); auto &d = update.c_updateDeleteChannelMessages();
auto channel = session().data().channelLoaded(d.vchannel_id().v); auto channel = session().data().channelLoaded(d.vchannel_id());
if (channel && !_handlingChannelDifference) { if (channel && !_handlingChannelDifference) {
if (channel->ptsRequesting()) { // skip global updates while getting channel difference if (channel->ptsRequesting()) { // skip global updates while getting channel difference
@ -1662,7 +1662,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
session().data().processWebpage(d.vwebpage()); session().data().processWebpage(d.vwebpage());
session().data().sendWebPageGamePollNotifications(); session().data().sendWebPageGamePollNotifications();
auto channel = session().data().channelLoaded(d.vchannel_id().v); auto channel = session().data().channelLoaded(d.vchannel_id());
if (channel && !_handlingChannelDifference) { if (channel && !_handlingChannelDifference) {
if (channel->ptsRequesting()) { // skip global updates while getting channel difference if (channel->ptsRequesting()) { // skip global updates while getting channel difference
return; return;
@ -1683,27 +1683,25 @@ void Updates::feedUpdate(const MTPUpdate &update) {
handleSendActionUpdate( handleSendActionUpdate(
peerFromUser(d.vuser_id()), peerFromUser(d.vuser_id()),
0, 0,
d.vuser_id().v, peerFromUser(d.vuser_id()),
d.vaction()); d.vaction());
} break; } break;
case mtpc_updateChatUserTyping: { case mtpc_updateChatUserTyping: {
auto &d = update.c_updateChatUserTyping(); auto &d = update.c_updateChatUserTyping();
const auto fromId = peerFromMTP(d.vfrom_id());
handleSendActionUpdate( handleSendActionUpdate(
peerFromChat(d.vchat_id()), peerFromChat(d.vchat_id()),
0, 0,
fromId, peerFromMTP(d.vfrom_id()),
d.vaction()); d.vaction());
} break; } break;
case mtpc_updateChannelUserTyping: { case mtpc_updateChannelUserTyping: {
const auto &d = update.c_updateChannelUserTyping(); const auto &d = update.c_updateChannelUserTyping();
const auto fromId = peerFromMTP(d.vfrom_id());
handleSendActionUpdate( handleSendActionUpdate(
peerFromChannel(d.vchannel_id()), peerFromChannel(d.vchannel_id()),
d.vtop_msg_id().value_or_empty(), d.vtop_msg_id().value_or_empty(),
fromId, peerFromMTP(d.vfrom_id()),
d.vaction()); d.vaction());
} break; } break;
@ -1729,7 +1727,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateUserStatus: { case mtpc_updateUserStatus: {
auto &d = update.c_updateUserStatus(); auto &d = update.c_updateUserStatus();
if (auto user = session().data().userLoaded(d.vuser_id().v)) { if (auto user = session().data().userLoaded(d.vuser_id())) {
switch (d.vstatus().type()) { switch (d.vstatus().type()) {
case mtpc_userStatusEmpty: user->onlineTill = 0; break; case mtpc_userStatusEmpty: user->onlineTill = 0; break;
case mtpc_userStatusRecently: case mtpc_userStatusRecently:
@ -1746,7 +1744,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
user, user,
Data::PeerUpdate::Flag::OnlineStatus); Data::PeerUpdate::Flag::OnlineStatus);
} }
if (d.vuser_id().v == session().userId()) { if (UserId(d.vuser_id()) == session().userId()) {
if (d.vstatus().type() == mtpc_userStatusOffline if (d.vstatus().type() == mtpc_userStatusOffline
|| d.vstatus().type() == mtpc_userStatusEmpty) { || d.vstatus().type() == mtpc_userStatusEmpty) {
updateOnline(true); updateOnline(true);
@ -1763,7 +1761,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateUserName: { case mtpc_updateUserName: {
auto &d = update.c_updateUserName(); auto &d = update.c_updateUserName();
if (auto user = session().data().userLoaded(d.vuser_id().v)) { if (auto user = session().data().userLoaded(d.vuser_id())) {
if (!user->isContact()) { if (!user->isContact()) {
user->setName( user->setName(
TextUtilities::SingleLine(qs(d.vfirst_name())), TextUtilities::SingleLine(qs(d.vfirst_name())),
@ -1782,7 +1780,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateUserPhoto: { case mtpc_updateUserPhoto: {
auto &d = update.c_updateUserPhoto(); auto &d = update.c_updateUserPhoto();
if (auto user = session().data().userLoaded(d.vuser_id().v)) { if (auto user = session().data().userLoaded(d.vuser_id())) {
user->setPhoto(d.vphoto()); user->setPhoto(d.vphoto());
user->loadUserpic(); user->loadUserpic();
// After that update we don't have enough information to // After that update we don't have enough information to
@ -1793,11 +1791,11 @@ void Updates::feedUpdate(const MTPUpdate &update) {
// //
//if (mtpIsTrue(d.vprevious()) || !user->userpicPhotoId()) { //if (mtpIsTrue(d.vprevious()) || !user->userpicPhotoId()) {
session().storage().remove(Storage::UserPhotosRemoveAfter( session().storage().remove(Storage::UserPhotosRemoveAfter(
user->bareId(), peerToUser(user->id),
user->userpicPhotoId())); user->userpicPhotoId()));
//} else { //} else {
// session().storage().add(Storage::UserPhotosAddNew( // session().storage().add(Storage::UserPhotosAddNew(
// user->bareId(), // peerToUser(user->id),
// user->userpicPhotoId())); // user->userpicPhotoId()));
//} //}
} }
@ -1831,7 +1829,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateUserPhone: { case mtpc_updateUserPhone: {
const auto &d = update.c_updateUserPhone(); const auto &d = update.c_updateUserPhone();
if (const auto user = session().data().userLoaded(d.vuser_id().v)) { if (const auto user = session().data().userLoaded(d.vuser_id())) {
const auto newPhone = qs(d.vphone()); const auto newPhone = qs(d.vphone());
if (newPhone != user->phone()) { if (newPhone != user->phone()) {
user->setPhone(newPhone); user->setPhone(newPhone);
@ -1914,8 +1912,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
auto &d = update.c_updatePrivacy(); auto &d = update.c_updatePrivacy();
const auto allChatsLoaded = [&](const MTPVector<MTPint> &ids) { const auto allChatsLoaded = [&](const MTPVector<MTPint> &ids) {
for (const auto &chatId : ids.v) { for (const auto &chatId : ids.v) {
if (!session().data().chatLoaded(chatId.v) if (!session().data().chatLoaded(chatId)
&& !session().data().channelLoaded(chatId.v)) { && !session().data().channelLoaded(chatId)) {
return false; return false;
} }
} }
@ -1999,7 +1997,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
} }
DEBUG_LOG(("API Error: " DEBUG_LOG(("API Error: "
"pinned chat not loaded for peer %1, folder: %2" "pinned chat not loaded for peer %1, folder: %2"
).arg(id ).arg(id.value
).arg(folderId ).arg(folderId
)); ));
return false; return false;
@ -2027,7 +2025,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateChannel: { case mtpc_updateChannel: {
auto &d = update.c_updateChannel(); auto &d = update.c_updateChannel();
if (const auto channel = session().data().channelLoaded(d.vchannel_id().v)) { if (const auto channel = session().data().channelLoaded(d.vchannel_id())) {
channel->inviter = UserId(0); channel->inviter = UserId(0);
if (channel->amIn()) { if (channel->amIn()) {
if (channel->isMegagroup() if (channel->isMegagroup()
@ -2049,7 +2047,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateChannelTooLong: { case mtpc_updateChannelTooLong: {
const auto &d = update.c_updateChannelTooLong(); const auto &d = update.c_updateChannelTooLong();
if (const auto channel = session().data().channelLoaded(d.vchannel_id().v)) { if (const auto channel = session().data().channelLoaded(d.vchannel_id())) {
const auto pts = d.vpts(); const auto pts = d.vpts();
if (!pts || channel->pts() < pts->v) { if (!pts || channel->pts() < pts->v) {
getChannelDifference(channel); getChannelDifference(channel);
@ -2108,7 +2106,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateChannelAvailableMessages: { case mtpc_updateChannelAvailableMessages: {
auto &d = update.c_updateChannelAvailableMessages(); auto &d = update.c_updateChannelAvailableMessages();
if (const auto channel = session().data().channelLoaded(d.vchannel_id().v)) { if (const auto channel = session().data().channelLoaded(d.vchannel_id())) {
channel->setAvailableMinId(d.vavailable_min_id().v); channel->setAvailableMinId(d.vavailable_min_id().v);
if (const auto history = session().data().historyLoaded(channel)) { if (const auto history = session().data().historyLoaded(channel)) {
history->clearUpTill(d.vavailable_min_id().v); history->clearUpTill(d.vavailable_min_id().v);

View file

@ -692,7 +692,7 @@ QString ApiWrap::exportDirectMessageLink(
if (inRepliesContext) { if (inRepliesContext) {
if (const auto rootId = item->replyToTop()) { if (const auto rootId = item->replyToTop()) {
const auto root = item->history()->owner().message( const auto root = item->history()->owner().message(
channel->bareId(), peerToChannel(channel->id),
rootId); rootId);
const auto sender = root const auto sender = root
? root->discussionPostOriginalSender() ? root->discussionPostOriginalSender()
@ -715,7 +715,7 @@ QString ApiWrap::exportDirectMessageLink(
} }
const auto base = linkChannel->hasUsername() const auto base = linkChannel->hasUsername()
? linkChannel->username ? linkChannel->username
: "c/" + QString::number(linkChannel->bareId()); : "c/" + QString::number(peerToChannel(linkChannel->id).bare);
const auto query = base const auto query = base
+ '/' + '/'
+ QString::number(linkItemId) + QString::number(linkItemId)
@ -776,7 +776,7 @@ void ApiWrap::requestContacts() {
for (const auto &contact : d.vcontacts().v) { for (const auto &contact : d.vcontacts().v) {
if (contact.type() != mtpc_contact) continue; if (contact.type() != mtpc_contact) continue;
const auto userId = contact.c_contact().vuser_id().v; const auto userId = UserId(contact.c_contact().vuser_id());
if (userId == _session->userId()) { if (userId == _session->userId()) {
_session->user()->setIsContact(true); _session->user()->setIsContact(true);
} }
@ -2267,7 +2267,7 @@ void ApiWrap::updatePrivacyLastSeens(const QVector<MTPPrivacyRule> &rules) {
for (const auto &item : result.v) { for (const auto &item : result.v) {
Assert(item.type() == mtpc_contactStatus); Assert(item.type() == mtpc_contactStatus);
auto &data = item.c_contactStatus(); auto &data = item.c_contactStatus();
if (auto user = _session->data().userLoaded(data.vuser_id().v)) { if (auto user = _session->data().userLoaded(data.vuser_id())) {
auto oldOnlineTill = user->onlineTill; auto oldOnlineTill = user->onlineTill;
auto newOnlineTill = OnlineTillFromStatus(data.vstatus(), oldOnlineTill); auto newOnlineTill = OnlineTillFromStatus(data.vstatus(), oldOnlineTill);
if (oldOnlineTill != newOnlineTill) { if (oldOnlineTill != newOnlineTill) {
@ -3566,7 +3566,7 @@ void ApiWrap::userPhotosDone(
} }
} }
_session->storage().add(Storage::UserPhotosAddSlice( _session->storage().add(Storage::UserPhotosAddSlice(
user->id, peerToUser(user->id),
std::move(photoIds), std::move(photoIds),
fullCount fullCount
)); ));
@ -4297,7 +4297,7 @@ void ApiWrap::sendSharedContact(
MTP_string(firstName), MTP_string(firstName),
MTP_string(lastName), MTP_string(lastName),
MTP_string(vcard), MTP_string(vcard),
MTP_int(userId)), MTP_int(userId.bare)), // #TODO ids
MTPReplyMarkup(), MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(), MTPVector<MTPMessageEntity>(),
MTP_int(views), MTP_int(views),
@ -5114,7 +5114,7 @@ void ApiWrap::clearPeerPhoto(not_null<PhotoData*> photo) {
MTP_vector<MTPInputPhoto>(1, photo->mtpInput()) MTP_vector<MTPInputPhoto>(1, photo->mtpInput())
)).send(); )).send();
_session->storage().remove(Storage::UserPhotosRemoveOne( _session->storage().remove(Storage::UserPhotosRemoveOne(
self->bareId(), peerToUser(self->id),
photo->id)); photo->id));
} }
} }
@ -5291,11 +5291,11 @@ auto ApiWrap::parsePrivacy(const QVector<MTPPrivacyRule> &rules)
}, [&](const MTPDprivacyValueAllowChatParticipants &data) { }, [&](const MTPDprivacyValueAllowChatParticipants &data) {
const auto &chats = data.vchats().v; const auto &chats = data.vchats().v;
always.reserve(always.size() + chats.size()); always.reserve(always.size() + chats.size());
for (const auto chatId : chats) { for (const auto &chatId : chats) {
const auto chat = _session->data().chatLoaded(chatId.v); const auto chat = _session->data().chatLoaded(chatId);
const auto peer = chat const auto peer = chat
? static_cast<PeerData*>(chat) ? static_cast<PeerData*>(chat)
: _session->data().channelLoaded(chatId.v); : _session->data().channelLoaded(chatId);
if (peer if (peer
&& !base::contains(never, peer) && !base::contains(never, peer)
&& !base::contains(always, peer)) { && !base::contains(always, peer)) {
@ -5319,11 +5319,11 @@ auto ApiWrap::parsePrivacy(const QVector<MTPPrivacyRule> &rules)
}, [&](const MTPDprivacyValueDisallowChatParticipants &data) { }, [&](const MTPDprivacyValueDisallowChatParticipants &data) {
const auto &chats = data.vchats().v; const auto &chats = data.vchats().v;
never.reserve(never.size() + chats.size()); never.reserve(never.size() + chats.size());
for (const auto chatId : chats) { for (const auto &chatId : chats) {
const auto chat = _session->data().chatLoaded(chatId.v); const auto chat = _session->data().chatLoaded(chatId);
const auto peer = chat const auto peer = chat
? static_cast<PeerData*>(chat) ? static_cast<PeerData*>(chat)
: _session->data().channelLoaded(chatId.v); : _session->data().channelLoaded(chatId);
if (peer if (peer
&& !base::contains(always, peer) && !base::contains(always, peer)
&& !base::contains(never, peer)) { && !base::contains(never, peer)) {

View file

@ -76,6 +76,15 @@ inline QString ToString(uint64 value) {
return QString::number(value); return QString::number(value);
} }
template <uchar Shift>
inline QString ToString(ChatIdType<Shift> value) {
return QString::number(value.bare);
}
inline QString ToString(PeerId value) {
return QString::number(value.value);
}
} // namespace details } // namespace details
template < template <

View file

@ -94,7 +94,7 @@ void ChatCreateDone(
} }
| [&](auto chats) { | [&](auto chats) {
return navigation->session().data().chat( return navigation->session().data().chat(
chats->front().c_chat().vid().v); chats->front().c_chat().vid());
} }
| [&](not_null<ChatData*> chat) { | [&](not_null<ChatData*> chat) {
if (!image.isNull()) { if (!image.isNull()) {
@ -268,7 +268,7 @@ AddContactBox::AddContactBox(
this, this,
st::defaultInputField, st::defaultInputField,
tr::lng_contact_phone(), tr::lng_contact_phone(),
ExtractPhonePrefix(session->user()->phone()), Ui::ExtractPhonePrefix(session->user()->phone()),
phone) phone)
, _invertOrder(langFirstNameGoesSecond()) { , _invertOrder(langFirstNameGoesSecond()) {
if (!phone.isEmpty()) { if (!phone.isEmpty()) {
@ -401,7 +401,7 @@ void AddContactBox::save() {
const auto extractUser = [&](const MTPImportedContact &data) { const auto extractUser = [&](const MTPImportedContact &data) {
return data.match([&](const MTPDimportedContact &data) { return data.match([&](const MTPDimportedContact &data) {
return (data.vclient_id().v == _contactId) return (data.vclient_id().v == _contactId)
? _session->data().userLoaded(data.vuser_id().v) ? _session->data().userLoaded(data.vuser_id())
: nullptr; : nullptr;
}); });
}; };
@ -687,7 +687,7 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
} }
| [&](auto chats) { | [&](auto chats) {
return _navigation->session().data().channel( return _navigation->session().data().channel(
chats->front().c_channel().vid().v); chats->front().c_channel().vid());
} }
| [&](not_null<ChannelData*> channel) { | [&](not_null<ChannelData*> channel) {
auto image = _photo->takeResultImage(); auto image = _photo->takeResultImage();

View file

@ -295,7 +295,7 @@ AdminLog::OwnedItem GenerateTextItem(
| (out ? Flag::f_out : Flag(0)); | (out ? Flag::f_out : Flag(0));
const auto clientFlags = MTPDmessage_ClientFlag::f_fake_history_item; const auto clientFlags = MTPDmessage_ClientFlag::f_fake_history_item;
const auto replyTo = 0; const auto replyTo = 0;
const auto viaBotId = 0; const auto viaBotId = UserId(0);
const auto item = history->makeMessage( const auto item = history->makeMessage(
++id, ++id,
flags, flags,
@ -402,14 +402,12 @@ BackgroundPreviewBox::BackgroundPreviewBox(
, _controller(controller) , _controller(controller)
, _text1(GenerateTextItem( , _text1(GenerateTextItem(
delegate(), delegate(),
_controller->session().data().history( _controller->session().data().history(PeerData::kServiceNotificationsId),
peerFromUser(PeerData::kServiceNotificationsId)),
tr::lng_background_text1(tr::now), tr::lng_background_text1(tr::now),
false)) false))
, _text2(GenerateTextItem( , _text2(GenerateTextItem(
delegate(), delegate(),
_controller->session().data().history( _controller->session().data().history(PeerData::kServiceNotificationsId),
peerFromUser(PeerData::kServiceNotificationsId)),
tr::lng_background_text2(tr::now), tr::lng_background_text2(tr::now),
true)) true))
, _paper(paper) , _paper(paper)

View file

@ -151,7 +151,7 @@ void ChangePhoneBox::EnterPhone::prepare() {
this, this,
st::defaultInputField, st::defaultInputField,
tr::lng_change_phone_new_title(), tr::lng_change_phone_new_title(),
ExtractPhonePrefix(_session->user()->phone()), Ui::ExtractPhonePrefix(_session->user()->phone()),
phoneValue); phoneValue);
_phone->resize(st::boxWidth - 2 * st::boxPadding.left(), _phone->height()); _phone->resize(st::boxWidth - 2 * st::boxPadding.left(), _phone->height());

View file

@ -304,9 +304,10 @@ void ConfirmBox::mouseReleaseEvent(QMouseEvent *e) {
_lastMousePos = e->globalPos(); _lastMousePos = e->globalPos();
updateHover(); updateHover();
if (const auto activated = ClickHandler::unpressed()) { if (const auto activated = ClickHandler::unpressed()) {
const auto guard = window(); ActivateClickHandler(window(), activated, e->button());
Ui::hideLayer(); crl::on_main(this, [=] {
ActivateClickHandler(guard, activated, e->button()); closeBox();
});
return; return;
} }
BoxContent::mouseReleaseEvent(e); BoxContent::mouseReleaseEvent(e);

View file

@ -71,14 +71,6 @@ void ShowPhoneBannedError(const QString &phone) {
[=] { SendToBannedHelp(phone); close(); })); [=] { SendToBannedHelp(phone); close(); }));
} }
QString ExtractPhonePrefix(const QString &phone) {
const auto pattern = phoneNumberParse(phone);
if (!pattern.isEmpty()) {
return phone.mid(0, pattern[0]);
}
return QString();
}
SentCodeField::SentCodeField( SentCodeField::SentCodeField(
QWidget *parent, QWidget *parent,
const style::InputField &st, const style::InputField &st,

View file

@ -22,7 +22,6 @@ class Session;
} // namespace Main } // namespace Main
void ShowPhoneBannedError(const QString &phone); void ShowPhoneBannedError(const QString &phone);
[[nodiscard]] QString ExtractPhonePrefix(const QString &phone);
class SentCodeField : public Ui::InputField { class SentCodeField : public Ui::InputField {
public: public:

View file

@ -182,11 +182,11 @@ QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() {
return result; return result;
}; };
const auto collectInputChats = [](const auto &peers) { const auto collectInputChats = [](const auto &peers) {
auto result = QVector<MTPint>(); auto result = QVector<MTPint>(); // #TODO ids
result.reserve(peers.size()); result.reserve(peers.size());
for (const auto peer : peers) { for (const auto peer : peers) {
if (!peer->isUser()) { if (!peer->isUser()) {
result.push_back(MTP_int(peer->bareId())); result.push_back(peerToBareMTPInt(peer->id));
} }
} }
return result; return result;

View file

@ -125,7 +125,7 @@ private:
} }
[[nodiscard]] uint64 TypeId(Flag flag) { [[nodiscard]] uint64 TypeId(Flag flag) {
return PeerIdFakeShift | static_cast<uint64>(flag); return PeerId(FakeChatId(static_cast<BareId>(flag))).value;
} }
TypeRow::TypeRow(Flag flag) : PeerListRow(TypeId(flag)) { TypeRow::TypeRow(Flag flag) : PeerListRow(TypeId(flag)) {

View file

@ -361,7 +361,11 @@ void PasscodeBox::closeReplacedBy() {
} }
void PasscodeBox::setPasswordFail(const MTP::Error &error) { void PasscodeBox::setPasswordFail(const MTP::Error &error) {
if (MTP::IsFloodError(error)) { setPasswordFail(error.type());
}
void PasscodeBox::setPasswordFail(const QString &type) {
if (MTP::IsFloodError(type)) {
closeReplacedBy(); closeReplacedBy();
_setRequest = 0; _setRequest = 0;
@ -378,20 +382,19 @@ void PasscodeBox::setPasswordFail(const MTP::Error &error) {
closeReplacedBy(); closeReplacedBy();
_setRequest = 0; _setRequest = 0;
const auto &err = error.type(); if (type == qstr("PASSWORD_HASH_INVALID")
if (err == qstr("PASSWORD_HASH_INVALID") || type == qstr("SRP_PASSWORD_CHANGED")) {
|| err == qstr("SRP_PASSWORD_CHANGED")) {
if (_oldPasscode->isHidden()) { if (_oldPasscode->isHidden()) {
_passwordReloadNeeded.fire({}); _passwordReloadNeeded.fire({});
closeBox(); closeBox();
} else { } else {
badOldPasscode(); badOldPasscode();
} }
} else if (err == qstr("SRP_ID_INVALID")) { } else if (type == qstr("SRP_ID_INVALID")) {
handleSrpIdInvalid(); handleSrpIdInvalid();
//} else if (err == qstr("NEW_PASSWORD_BAD")) { //} else if (type == qstr("NEW_PASSWORD_BAD")) {
//} else if (err == qstr("NEW_SALT_INVALID")) { //} else if (type == qstr("NEW_SALT_INVALID")) {
} else if (err == qstr("EMAIL_INVALID")) { } else if (type == qstr("EMAIL_INVALID")) {
_emailError = tr::lng_cloud_password_bad_email(tr::now); _emailError = tr::lng_cloud_password_bad_email(tr::now);
_recoverEmail->setFocus(); _recoverEmail->setFocus();
_recoverEmail->showError(); _recoverEmail->showError();
@ -682,12 +685,15 @@ void PasscodeBox::serverError() {
} }
bool PasscodeBox::handleCustomCheckError(const MTP::Error &error) { bool PasscodeBox::handleCustomCheckError(const MTP::Error &error) {
const auto &type = error.type(); return handleCustomCheckError(error.type());
if (MTP::IsFloodError(error) }
bool PasscodeBox::handleCustomCheckError(const QString &type) {
if (MTP::IsFloodError(type)
|| type == qstr("PASSWORD_HASH_INVALID") || type == qstr("PASSWORD_HASH_INVALID")
|| type == qstr("SRP_PASSWORD_CHANGED") || type == qstr("SRP_PASSWORD_CHANGED")
|| type == qstr("SRP_ID_INVALID")) { || type == qstr("SRP_ID_INVALID")) {
setPasswordFail(error); setPasswordFail(type);
return true; return true;
} }
return false; return false;

View file

@ -56,6 +56,7 @@ public:
rpl::producer<> clearUnconfirmedPassword() const; rpl::producer<> clearUnconfirmedPassword() const;
bool handleCustomCheckError(const MTP::Error &error); bool handleCustomCheckError(const MTP::Error &error);
bool handleCustomCheckError(const QString &type);
protected: protected:
void prepare() override; void prepare() override;
@ -82,6 +83,7 @@ private:
void setPasswordDone(const QByteArray &newPasswordBytes); void setPasswordDone(const QByteArray &newPasswordBytes);
void setPasswordFail(const MTP::Error &error); void setPasswordFail(const MTP::Error &error);
void setPasswordFail(const QString &type);
void setPasswordFail( void setPasswordFail(
const QByteArray &newPasswordBytes, const QByteArray &newPasswordBytes,
const QString &email, const QString &email,

View file

@ -89,8 +89,8 @@ void PeerListBox::createMultiSelect() {
if (_controller->handleDeselectForeignRow(itemId)) { if (_controller->handleDeselectForeignRow(itemId)) {
return; return;
} }
if (const auto peer = _controller->session().data().peerLoaded(itemId)) { if (const auto peer = _controller->session().data().peerLoaded(PeerId(itemId))) {
if (const auto row = peerListFindRow(peer->id)) { if (const auto row = peerListFindRow(itemId)) {
content()->changeCheckState(row, false, anim::type::normal); content()->changeCheckState(row, false, anim::type::normal);
update(); update();
} }
@ -278,11 +278,11 @@ void PeerListController::search(const QString &query) {
} }
void PeerListController::peerListSearchAddRow(not_null<PeerData*> peer) { void PeerListController::peerListSearchAddRow(not_null<PeerData*> peer) {
if (auto row = delegate()->peerListFindRow(peer->id)) { if (auto row = delegate()->peerListFindRow(peer->id.value)) {
Assert(row->id() == row->peer()->id); Assert(row->id() == row->peer()->id.value);
delegate()->peerListAppendFoundRow(row); delegate()->peerListAppendFoundRow(row);
} else if (auto row = createSearchRow(peer)) { } else if (auto row = createSearchRow(peer)) {
Assert(row->id() == row->peer()->id); Assert(row->id() == row->peer()->id.value);
delegate()->peerListAppendSearchRow(std::move(row)); delegate()->peerListAppendSearchRow(std::move(row));
} }
} }
@ -356,7 +356,7 @@ void PeerListBox::addSelectItem(
? tr::lng_replies_messages(tr::now) ? tr::lng_replies_messages(tr::now)
: peer->shortName(); : peer->shortName();
addSelectItem( addSelectItem(
peer->id, peer->id.value,
text, text,
PaintUserpicCallback(peer, respect), PaintUserpicCallback(peer, respect),
animated); animated);
@ -423,7 +423,7 @@ auto PeerListBox::collectSelectedRows()
result.reserve(items.size()); result.reserve(items.size());
for (const auto itemId : items) { for (const auto itemId : items) {
if (!_controller->isForeignRow(itemId)) { if (!_controller->isForeignRow(itemId)) {
result.push_back(_controller->session().data().peer(itemId)); result.push_back(_controller->session().data().peer(PeerId(itemId)));
} }
} }
} }
@ -431,7 +431,7 @@ auto PeerListBox::collectSelectedRows()
} }
PeerListRow::PeerListRow(not_null<PeerData*> peer) PeerListRow::PeerListRow(not_null<PeerData*> peer)
: PeerListRow(peer, peer->id) { : PeerListRow(peer, peer->id.value) {
} }
PeerListRow::PeerListRow(not_null<PeerData*> peer, PeerListRowId id) PeerListRow::PeerListRow(not_null<PeerData*> peer, PeerListRowId id)
@ -914,7 +914,7 @@ void PeerListContent::addRowEntry(not_null<PeerListRow*> row) {
addToSearchIndex(row); addToSearchIndex(row);
} }
if (_controller->isRowSelected(row)) { if (_controller->isRowSelected(row)) {
Assert(row->special() || row->id() == row->peer()->id); Assert(row->special() || row->id() == row->peer()->id.value);
changeCheckState(row, true, anim::type::instant); changeCheckState(row, true, anim::type::instant);
} }
} }
@ -1772,7 +1772,7 @@ void PeerListContent::restoreState(
auto searchWords = TextUtilities::PrepareSearchWords(query); auto searchWords = TextUtilities::PrepareSearchWords(query);
setSearchQuery(query, searchWords.join(' ')); setSearchQuery(query, searchWords.join(' '));
for (auto peer : state->filterResults) { for (auto peer : state->filterResults) {
if (auto existingRow = findRow(peer->id)) { if (auto existingRow = findRow(peer->id.value)) {
_filterResults.push_back(existingRow); _filterResults.push_back(existingRow);
} else if (auto row = _controller->createSearchRow(peer)) { } else if (auto row = _controller->createSearchRow(peer)) {
appendSearchRow(std::move(row)); appendSearchRow(std::move(row));

View file

@ -369,7 +369,7 @@ std::unique_ptr<PeerListRow> ChatsListBoxController::createSearchRow(not_null<Pe
} }
bool ChatsListBoxController::appendRow(not_null<History*> history) { bool ChatsListBoxController::appendRow(not_null<History*> history) {
if (auto row = delegate()->peerListFindRow(history->peer->id)) { if (auto row = delegate()->peerListFindRow(history->peer->id.value)) {
updateRowHook(static_cast<Row*>(row)); updateRowHook(static_cast<Row*>(row));
return false; return false;
} }
@ -451,7 +451,7 @@ void ContactsBoxController::rowClicked(not_null<PeerListRow*> row) {
} }
bool ContactsBoxController::appendRow(not_null<UserData*> user) { bool ContactsBoxController::appendRow(not_null<UserData*> user) {
if (auto row = delegate()->peerListFindRow(user->id)) { if (auto row = delegate()->peerListFindRow(user->id.value)) {
updateRowHook(row); updateRowHook(row);
return false; return false;
} }

View file

@ -46,7 +46,7 @@ auto PeerListsBox::collectSelectedRows()
return false; return false;
}(); }();
if (!foreign) { if (!foreign) {
result.push_back(session->data().peer(itemId)); result.push_back(session->data().peer(PeerId(itemId)));
} }
} }
} }
@ -108,10 +108,10 @@ void PeerListsBox::createMultiSelect() {
} }
} }
const auto session = &firstController()->session(); const auto session = &firstController()->session();
if (const auto peer = session->data().peerLoaded(itemId)) { if (const auto peer = session->data().peerLoaded(PeerId(itemId))) {
const auto id = peer->id; const auto id = peer->id;
for (const auto &list : _lists) { for (const auto &list : _lists) {
if (const auto row = list.delegate->peerListFindRow(id)) { if (const auto row = list.delegate->peerListFindRow(id.value)) {
list.content->changeCheckState( list.content->changeCheckState(
row, row,
false, false,
@ -385,7 +385,7 @@ void PeerListsBox::addSelectItem(
not_null<PeerData*> peer, not_null<PeerData*> peer,
anim::type animated) { anim::type animated) {
addSelectItem( addSelectItem(
peer->id, peer->id.value,
peer->shortName(), peer->shortName(),
PaintUserpicCallback(peer, false), PaintUserpicCallback(peer, false),
animated); animated);

View file

@ -661,12 +661,12 @@ void AddSpecialBoxController::editAdminDone(
using Flag = MTPDchannelParticipantCreator::Flag; using Flag = MTPDchannelParticipantCreator::Flag;
_additional.applyParticipant(MTP_channelParticipantCreator( _additional.applyParticipant(MTP_channelParticipantCreator(
MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank), MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank),
MTP_int(user->bareId()), peerToBareMTPInt(user->id),
rights, rights,
MTP_string(rank))); MTP_string(rank)));
} else if (rights.c_chatAdminRights().vflags().v == 0) { } else if (rights.c_chatAdminRights().vflags().v == 0) {
_additional.applyParticipant(MTP_channelParticipant( _additional.applyParticipant(MTP_channelParticipant(
MTP_int(user->bareId()), peerToBareMTPInt(user->id),
MTP_int(date))); MTP_int(date)));
} else { } else {
using Flag = MTPDchannelParticipantAdmin::Flag; using Flag = MTPDchannelParticipantAdmin::Flag;
@ -674,11 +674,11 @@ void AddSpecialBoxController::editAdminDone(
_additional.applyParticipant(MTP_channelParticipantAdmin( _additional.applyParticipant(MTP_channelParticipantAdmin(
MTP_flags(Flag::f_can_edit MTP_flags(Flag::f_can_edit
| (rank.isEmpty() ? Flag(0) : Flag::f_rank)), | (rank.isEmpty() ? Flag(0) : Flag::f_rank)),
MTP_int(user->bareId()), peerToBareMTPInt(user->id),
MTPint(), // inviter_id MTPint(), // inviter_id
MTP_int(alreadyPromotedBy peerToBareMTPInt(alreadyPromotedBy
? alreadyPromotedBy->bareId() ? alreadyPromotedBy->id
: user->session().userId()), : user->session().userPeerId()),
MTP_int(date), MTP_int(date),
rights, rights,
MTP_string(rank))); MTP_string(rank)));
@ -763,7 +763,7 @@ void AddSpecialBoxController::editRestrictedDone(
if (Data::ChatBannedRightsFlags(rights) == 0) { if (Data::ChatBannedRightsFlags(rights) == 0) {
if (const auto user = participant->asUser()) { if (const auto user = participant->asUser()) {
_additional.applyParticipant(MTP_channelParticipant( _additional.applyParticipant(MTP_channelParticipant(
MTP_int(user->bareId()), peerToBareMTPInt(user->id),
MTP_int(date))); MTP_int(date)));
} else { } else {
_additional.setExternal(participant); _additional.setExternal(participant);
@ -777,14 +777,10 @@ void AddSpecialBoxController::editRestrictedDone(
MTP_flags(kicked MTP_flags(kicked
? MTPDchannelParticipantBanned::Flag::f_left ? MTPDchannelParticipantBanned::Flag::f_left
: MTPDchannelParticipantBanned::Flag(0)), : MTPDchannelParticipantBanned::Flag(0)),
(participant->isUser() peerToMTP(participant->id),
? MTP_peerUser(MTP_int(participant->bareId())) peerToBareMTPInt(alreadyRestrictedBy
: participant->isChat() ? alreadyRestrictedBy->id
? MTP_peerChat(MTP_int(participant->bareId())) : participant->session().userPeerId()),
: MTP_peerChannel(MTP_int(participant->bareId()))),
MTP_int(alreadyRestrictedBy
? alreadyRestrictedBy->bareId()
: participant->session().userId()),
MTP_int(date), MTP_int(date),
rights)); rights));
} }
@ -861,7 +857,7 @@ void AddSpecialBoxController::kickUser(
} }
bool AddSpecialBoxController::appendRow(not_null<PeerData*> participant) { bool AddSpecialBoxController::appendRow(not_null<PeerData*> participant) {
if (delegate()->peerListFindRow(participant->id) if (delegate()->peerListFindRow(participant->id.value)
|| (_excludeSelf && participant->isSelf())) { || (_excludeSelf && participant->isSelf())) {
return false; return false;
} }
@ -870,7 +866,7 @@ bool AddSpecialBoxController::appendRow(not_null<PeerData*> participant) {
} }
bool AddSpecialBoxController::prependRow(not_null<UserData*> user) { bool AddSpecialBoxController::prependRow(not_null<UserData*> user) {
if (delegate()->peerListFindRow(user->id)) { if (delegate()->peerListFindRow(user->id.value)) {
return false; return false;
} }
delegate()->peerListPrependRow(createRow(user)); delegate()->peerListPrependRow(createRow(user));

View file

@ -31,7 +31,7 @@ constexpr auto kMaxUserFirstLastName = 64; // See also add_contact_box.
QString UserPhone(not_null<UserData*> user) { QString UserPhone(not_null<UserData*> user) {
const auto phone = user->phone(); const auto phone = user->phone();
return phone.isEmpty() return phone.isEmpty()
? user->owner().findContactPhone(user->bareId()) ? user->owner().findContactPhone(peerToUser(user->id))
: phone; : phone;
} }

View file

@ -82,7 +82,7 @@ int Controller::contentWidth() const {
void Controller::prepare() { void Controller::prepare() {
const auto appendRow = [&](not_null<PeerData*> chat) { const auto appendRow = [&](not_null<PeerData*> chat) {
if (delegate()->peerListFindRow(chat->id)) { if (delegate()->peerListFindRow(chat->id.value)) {
return; return;
} }
auto row = std::make_unique<PeerListRow>(chat); auto row = std::make_unique<PeerListRow>(chat);

View file

@ -609,7 +609,7 @@ UserData *ParticipantsAdditionalData::applyCreator(
UserData *ParticipantsAdditionalData::applyAdmin( UserData *ParticipantsAdditionalData::applyAdmin(
const MTPDchannelParticipantAdmin &data) { const MTPDchannelParticipantAdmin &data) {
const auto user = _peer->owner().userLoaded(data.vuser_id().v); const auto user = _peer->owner().userLoaded(UserId(data.vuser_id().v));
if (!user) { if (!user) {
return nullptr; return nullptr;
} else if (const auto chat = _peer->asChat()) { } else if (const auto chat = _peer->asChat()) {
@ -633,7 +633,7 @@ UserData *ParticipantsAdditionalData::applyAdmin(
} else { } else {
_adminRanks.remove(user); _adminRanks.remove(user);
} }
if (const auto by = _peer->owner().userLoaded(data.vpromoted_by().v)) { if (const auto by = _peer->owner().userLoaded(data.vpromoted_by())) {
const auto i = _adminPromotedBy.find(user); const auto i = _adminPromotedBy.find(user);
if (i == _adminPromotedBy.end()) { if (i == _adminPromotedBy.end()) {
_adminPromotedBy.emplace(user, by); _adminPromotedBy.emplace(user, by);
@ -648,7 +648,7 @@ UserData *ParticipantsAdditionalData::applyAdmin(
} }
UserData *ParticipantsAdditionalData::applyRegular(MTPint userId) { UserData *ParticipantsAdditionalData::applyRegular(MTPint userId) {
const auto user = _peer->owner().userLoaded(userId.v); const auto user = _peer->owner().userLoaded(userId);
if (!user) { if (!user) {
return nullptr; return nullptr;
} else if (const auto chat = _peer->asChat()) { } else if (const auto chat = _peer->asChat()) {
@ -687,7 +687,7 @@ PeerData *ParticipantsAdditionalData::applyBanned(
_kicked.erase(participant); _kicked.erase(participant);
} }
_restrictedRights[participant] = data.vbanned_rights(); _restrictedRights[participant] = data.vbanned_rights();
if (const auto by = _peer->owner().userLoaded(data.vkicked_by().v)) { if (const auto by = _peer->owner().userLoaded(data.vkicked_by())) {
const auto i = _restrictedBy.find(participant); const auto i = _restrictedBy.find(participant);
if (i == _restrictedBy.end()) { if (i == _restrictedBy.end()) {
_restrictedBy.emplace(participant, by); _restrictedBy.emplace(participant, by);
@ -727,7 +727,7 @@ ParticipantsOnlineSorter::ParticipantsOnlineSorter(
Data::PeerUpdate::Flag::OnlineStatus Data::PeerUpdate::Flag::OnlineStatus
) | rpl::start_with_next([=](const Data::PeerUpdate &update) { ) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
const auto peerId = update.peer->id; const auto peerId = update.peer->id;
if (const auto row = _delegate->peerListFindRow(peerId)) { if (const auto row = _delegate->peerListFindRow(peerId.value)) {
row->refreshStatus(); row->refreshStatus();
sortDelayed(); sortDelayed();
} }
@ -829,7 +829,7 @@ void ParticipantsBoxController::setupListChangeViewers() {
return; return;
} }
} }
if (const auto row = delegate()->peerListFindRow(user->id)) { if (const auto row = delegate()->peerListFindRow(user->id.value)) {
delegate()->peerListPartitionRows([&](const PeerListRow &row) { delegate()->peerListPartitionRows([&](const PeerListRow &row) {
return (row.peer() == user); return (row.peer() == user);
}); });
@ -845,7 +845,7 @@ void ParticipantsBoxController::setupListChangeViewers() {
channel->owner().megagroupParticipantRemoved( channel->owner().megagroupParticipantRemoved(
channel channel
) | rpl::start_with_next([=](not_null<UserData*> user) { ) | rpl::start_with_next([=](not_null<UserData*> user) {
if (const auto row = delegate()->peerListFindRow(user->id)) { if (const auto row = delegate()->peerListFindRow(user->id.value)) {
delegate()->peerListRemoveRow(row); delegate()->peerListRemoveRow(row);
} }
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
@ -1459,8 +1459,6 @@ void ParticipantsBoxController::rowActionClicked(
base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu( base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
QWidget *parent, QWidget *parent,
not_null<PeerListRow*> row) { not_null<PeerListRow*> row) {
Expects(row->peer()->isUser());
const auto chat = _peer->asChat(); const auto chat = _peer->asChat();
const auto channel = _peer->asChannel(); const auto channel = _peer->asChannel();
const auto participant = row->peer(); const auto participant = row->peer();
@ -1468,7 +1466,11 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
auto result = base::make_unique_q<Ui::PopupMenu>(parent); auto result = base::make_unique_q<Ui::PopupMenu>(parent);
if (_navigation) { if (_navigation) {
result->addAction( result->addAction(
tr::lng_context_view_profile(tr::now), (participant->isUser()
? tr::lng_context_view_profile
: participant->isBroadcast()
? tr::lng_context_view_channel
: tr::lng_context_view_group)(tr::now),
crl::guard(this, [=] { crl::guard(this, [=] {
_navigation->showPeerInfo(participant); })); _navigation->showPeerInfo(participant); }));
} }
@ -1509,12 +1511,12 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
: tr::lng_context_promote_admin)(tr::now), : tr::lng_context_promote_admin)(tr::now),
crl::guard(this, [=] { showAdmin(user); })); crl::guard(this, [=] { showAdmin(user); }));
} }
if (_additional.canRestrictParticipant(participant)) { if (user && _additional.canRestrictParticipant(participant)) {
const auto canRestrictWithoutKick = [&] { const auto canRestrictWithoutKick = [&] {
if (const auto chat = _peer->asChat()) { if (const auto chat = _peer->asChat()) {
return chat->amCreator(); return chat->amCreator();
} }
return _peer->isMegagroup(); return _peer->isMegagroup() && !_peer->isGigagroup();
}(); }();
if (canRestrictWithoutKick) { if (canRestrictWithoutKick) {
result->addAction( result->addAction(
@ -1522,7 +1524,7 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
crl::guard(this, [=] { showRestricted(user); })); crl::guard(this, [=] { showRestricted(user); }));
} }
} }
if (_additional.canRemoveParticipant(participant)) { if (user && _additional.canRemoveParticipant(participant)) {
if (!_additional.isKicked(participant)) { if (!_additional.isKicked(participant)) {
const auto isGroup = _peer->isChat() || _peer->isMegagroup(); const auto isGroup = _peer->isChat() || _peer->isMegagroup();
result->addAction( result->addAction(
@ -1591,12 +1593,12 @@ void ParticipantsBoxController::editAdminDone(
using Flag = MTPDchannelParticipantCreator::Flag; using Flag = MTPDchannelParticipantCreator::Flag;
_additional.applyParticipant(MTP_channelParticipantCreator( _additional.applyParticipant(MTP_channelParticipantCreator(
MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank), MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank),
MTP_int(user->bareId()), peerToBareMTPInt(user->id),
rights, rights,
MTP_string(rank))); MTP_string(rank)));
} else if (rights.c_chatAdminRights().vflags().v == 0) { } else if (rights.c_chatAdminRights().vflags().v == 0) {
_additional.applyParticipant(MTP_channelParticipant( _additional.applyParticipant(MTP_channelParticipant(
MTP_int(user->bareId()), peerToBareMTPInt(user->id),
MTP_int(date))); MTP_int(date)));
if (_role == Role::Admins) { if (_role == Role::Admins) {
removeRow(user); removeRow(user);
@ -1607,11 +1609,11 @@ void ParticipantsBoxController::editAdminDone(
_additional.applyParticipant(MTP_channelParticipantAdmin( _additional.applyParticipant(MTP_channelParticipantAdmin(
MTP_flags(Flag::f_can_edit MTP_flags(Flag::f_can_edit
| (rank.isEmpty() ? Flag(0) : Flag::f_rank)), | (rank.isEmpty() ? Flag(0) : Flag::f_rank)),
MTP_int(user->bareId()), peerToBareMTPInt(user->id),
MTPint(), // inviter_id MTPint(), // inviter_id
MTP_int(alreadyPromotedBy peerToBareMTPInt(alreadyPromotedBy
? alreadyPromotedBy->bareId() ? alreadyPromotedBy->id
: user->session().userId()), : user->session().userPeerId()),
MTP_int(date), MTP_int(date),
rights, rights,
MTP_string(rank))); MTP_string(rank)));
@ -1667,7 +1669,7 @@ void ParticipantsBoxController::editRestrictedDone(
if (Data::ChatBannedRightsFlags(rights) == 0) { if (Data::ChatBannedRightsFlags(rights) == 0) {
if (user) { if (user) {
_additional.applyParticipant(MTP_channelParticipant( _additional.applyParticipant(MTP_channelParticipant(
MTP_int(user->bareId()), peerToBareMTPInt(user->id),
MTP_int(date))); MTP_int(date)));
} else { } else {
_additional.setExternal(participant); _additional.setExternal(participant);
@ -1684,14 +1686,10 @@ void ParticipantsBoxController::editRestrictedDone(
MTP_flags(kicked MTP_flags(kicked
? MTPDchannelParticipantBanned::Flag::f_left ? MTPDchannelParticipantBanned::Flag::f_left
: MTPDchannelParticipantBanned::Flag(0)), : MTPDchannelParticipantBanned::Flag(0)),
(participant->isUser() peerToMTP(participant->id),
? MTP_peerUser(MTP_int(participant->bareId())) peerToBareMTPInt(alreadyRestrictedBy
: participant->isChat() ? alreadyRestrictedBy->id
? MTP_peerChat(MTP_int(participant->bareId())) : participant->session().userPeerId()),
: MTP_peerChannel(MTP_int(participant->bareId()))),
MTP_int(alreadyRestrictedBy
? alreadyRestrictedBy->bareId()
: participant->session().userId()),
MTP_int(date), MTP_int(date),
rights)); rights));
if (kicked) { if (kicked) {
@ -1734,7 +1732,7 @@ void ParticipantsBoxController::kickParticipant(not_null<PeerData*> participant)
void ParticipantsBoxController::unkickParticipant(not_null<UserData*> user) { void ParticipantsBoxController::unkickParticipant(not_null<UserData*> user) {
_editBox = nullptr; _editBox = nullptr;
if (const auto row = delegate()->peerListFindRow(user->id)) { if (const auto row = delegate()->peerListFindRow(user->id.value)) {
delegate()->peerListRemoveRow(row); delegate()->peerListRemoveRow(row);
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
} }
@ -1750,7 +1748,7 @@ void ParticipantsBoxController::kickParticipantSure(
? *restrictedRights ? *restrictedRights
: ChannelData::EmptyRestrictedRights(participant); : ChannelData::EmptyRestrictedRights(participant);
if (const auto row = delegate()->peerListFindRow(participant->id)) { if (const auto row = delegate()->peerListFindRow(participant->id.value)) {
delegate()->peerListRemoveRow(row); delegate()->peerListRemoveRow(row);
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
} }
@ -1800,7 +1798,7 @@ void ParticipantsBoxController::removeAdminSure(not_null<UserData*> user) {
void ParticipantsBoxController::removeKickedWithRow( void ParticipantsBoxController::removeKickedWithRow(
not_null<PeerData*> participant) { not_null<PeerData*> participant) {
if (const auto row = delegate()->peerListFindRow(participant->id)) { if (const auto row = delegate()->peerListFindRow(participant->id.value)) {
removeKicked(row, participant); removeKicked(row, participant);
} else { } else {
removeKicked(participant); removeKicked(participant);
@ -1826,7 +1824,7 @@ void ParticipantsBoxController::removeKicked(
} }
bool ParticipantsBoxController::appendRow(not_null<PeerData*> participant) { bool ParticipantsBoxController::appendRow(not_null<PeerData*> participant) {
if (delegate()->peerListFindRow(participant->id)) { if (delegate()->peerListFindRow(participant->id.value)) {
recomputeTypeFor(participant); recomputeTypeFor(participant);
return false; return false;
} else if (auto row = createRow(participant)) { } else if (auto row = createRow(participant)) {
@ -1840,7 +1838,7 @@ bool ParticipantsBoxController::appendRow(not_null<PeerData*> participant) {
} }
bool ParticipantsBoxController::prependRow(not_null<PeerData*> participant) { bool ParticipantsBoxController::prependRow(not_null<PeerData*> participant) {
if (const auto row = delegate()->peerListFindRow(participant->id)) { if (const auto row = delegate()->peerListFindRow(participant->id.value)) {
recomputeTypeFor(participant); recomputeTypeFor(participant);
refreshCustomStatus(row); refreshCustomStatus(row);
if (_role == Role::Admins) { if (_role == Role::Admins) {
@ -1859,7 +1857,7 @@ bool ParticipantsBoxController::prependRow(not_null<PeerData*> participant) {
} }
bool ParticipantsBoxController::removeRow(not_null<PeerData*> participant) { bool ParticipantsBoxController::removeRow(not_null<PeerData*> participant) {
if (auto row = delegate()->peerListFindRow(participant->id)) { if (auto row = delegate()->peerListFindRow(participant->id.value)) {
if (_role == Role::Admins) { if (_role == Role::Admins) {
// Perhaps we are removing an admin from search results. // Perhaps we are removing an admin from search results.
row->setCustomStatus(tr::lng_channel_admin_status_not_admin(tr::now)); row->setCustomStatus(tr::lng_channel_admin_status_not_admin(tr::now));
@ -1945,7 +1943,7 @@ void ParticipantsBoxController::recomputeTypeFor(
if (_role != Role::Profile) { if (_role != Role::Profile) {
return; return;
} }
if (const auto row = delegate()->peerListFindRow(participant->id)) { if (const auto row = delegate()->peerListFindRow(participant->id.value)) {
static_cast<Row*>(row)->setType(computeType(participant)); static_cast<Row*>(row)->setType(computeType(participant));
} }
} }

View file

@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h" #include "main/main_session.h"
#include "boxes/add_contact_box.h" #include "boxes/add_contact_box.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
#include "boxes/single_choice_box.h"
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_participants_box.h"
#include "boxes/peers/edit_peer_type_box.h" #include "boxes/peers/edit_peer_type_box.h"
@ -20,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/edit_peer_invite_links.h" #include "boxes/peers/edit_peer_invite_links.h"
#include "boxes/peers/edit_linked_chat_box.h" #include "boxes/peers/edit_linked_chat_box.h"
#include "boxes/stickers_box.h" #include "boxes/stickers_box.h"
#include "ui/boxes/single_choice_box.h"
#include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/emoji_suggestions_widget.h"
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"

View file

@ -766,7 +766,7 @@ void AdminsController::prepare() {
owner.processUsers(data.vusers()); owner.processUsers(data.vusers());
for (const auto &admin : data.vadmins().v) { for (const auto &admin : data.vadmins().v) {
admin.match([&](const MTPDchatAdminWithInvites &data) { admin.match([&](const MTPDchatAdminWithInvites &data) {
const auto adminId = data.vadmin_id().v; const auto adminId = data.vadmin_id();
if (const auto user = owner.userLoaded(adminId)) { if (const auto user = owner.userLoaded(adminId)) {
if (!user->isSelf()) { if (!user->isSelf()) {
appendRow(user, data.vinvites_count().v); appendRow(user, data.vinvites_count().v);

View file

@ -264,7 +264,7 @@ void ShareBox::prepare() {
applyFilterUpdate(query); applyFilterUpdate(query);
}); });
_select->setItemRemovedCallback([=](uint64 itemId) { _select->setItemRemovedCallback([=](uint64 itemId) {
if (const auto peer = _descriptor.session->data().peerLoaded(itemId)) { if (const auto peer = _descriptor.session->data().peerLoaded(PeerId(itemId))) {
_inner->peerUnselected(peer); _inner->peerUnselected(peer);
selectedChanged(); selectedChanged();
update(); update();
@ -610,7 +610,7 @@ void ShareBox::addPeerToMultiSelect(PeerData *peer, bool skipAnimation) {
using AddItemWay = Ui::MultiSelect::AddItemWay; using AddItemWay = Ui::MultiSelect::AddItemWay;
auto addItemWay = skipAnimation ? AddItemWay::SkipAnimation : AddItemWay::Default; auto addItemWay = skipAnimation ? AddItemWay::SkipAnimation : AddItemWay::Default;
_select->addItem( _select->addItem(
peer->id, peer->id.value,
peer->isSelf() ? tr::lng_saved_short(tr::now) : peer->shortName(), peer->isSelf() ? tr::lng_saved_short(tr::now) : peer->shortName(),
st::activeButtonBg, st::activeButtonBg,
PaintUserpicCallback(peer, true), PaintUserpicCallback(peer, true),
@ -622,7 +622,7 @@ void ShareBox::innerSelectedChanged(PeerData *peer, bool checked) {
addPeerToMultiSelect(peer); addPeerToMultiSelect(peer);
//_select->clearQuery(); //_select->clearQuery();
} else { } else {
_select->removeItem(peer->id); _select->removeItem(peer->id.value);
} }
selectedChanged(); selectedChanged();
update(); update();
@ -1285,25 +1285,25 @@ QString AppendShareGameScoreUrl(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const QString &url, const QString &url,
const FullMsgId &fullId) { const FullMsgId &fullId) {
auto shareHashData = QByteArray(0x10, Qt::Uninitialized); auto shareHashData = QByteArray(0x20, Qt::Uninitialized);
auto shareHashDataInts = reinterpret_cast<int32*>(shareHashData.data()); auto shareHashDataInts = reinterpret_cast<uint64*>(shareHashData.data());
auto channel = fullId.channel auto channel = fullId.channel
? session->data().channelLoaded(fullId.channel) ? session->data().channelLoaded(fullId.channel)
: static_cast<ChannelData*>(nullptr); : static_cast<ChannelData*>(nullptr);
auto channelAccessHash = channel ? channel->access : 0ULL; auto channelAccessHash = uint64(channel ? channel->access : 0);
auto channelAccessHashInts = reinterpret_cast<int32*>(&channelAccessHash); auto channelAccessHashInts = reinterpret_cast<int32*>(&channelAccessHash);
shareHashDataInts[0] = session->userId(); shareHashDataInts[0] = session->userId().bare;
shareHashDataInts[1] = fullId.channel; shareHashDataInts[1] = fullId.channel.bare;
shareHashDataInts[2] = fullId.msg; shareHashDataInts[2] = fullId.msg;
shareHashDataInts[3] = channelAccessHashInts[0]; shareHashDataInts[3] = channelAccessHash;
// Count SHA1() of data. // Count SHA1() of data.
auto key128Size = 0x10; auto key128Size = 0x10;
auto shareHashEncrypted = QByteArray(key128Size + shareHashData.size(), Qt::Uninitialized); auto shareHashEncrypted = QByteArray(key128Size + shareHashData.size(), Qt::Uninitialized);
hashSha1(shareHashData.constData(), shareHashData.size(), shareHashEncrypted.data()); hashSha1(shareHashData.constData(), shareHashData.size(), shareHashEncrypted.data());
// Mix in channel access hash to the first 64 bits of SHA1 of data. //// Mix in channel access hash to the first 64 bits of SHA1 of data.
*reinterpret_cast<uint64*>(shareHashEncrypted.data()) ^= *reinterpret_cast<uint64*>(channelAccessHashInts); //*reinterpret_cast<uint64*>(shareHashEncrypted.data()) ^= channelAccessHash;
// Encrypt data. // Encrypt data.
if (!session->local().encrypt(shareHashData.constData(), shareHashEncrypted.data() + key128Size, shareHashData.size(), shareHashEncrypted.constData())) { if (!session->local().encrypt(shareHashData.constData(), shareHashEncrypted.data() + key128Size, shareHashData.size(), shareHashEncrypted.constData())) {
@ -1335,7 +1335,7 @@ void ShareGameScoreByHash(
auto key128Size = 0x10; auto key128Size = 0x10;
auto hashEncrypted = QByteArray::fromBase64(hash.toLatin1(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); auto hashEncrypted = QByteArray::fromBase64(hash.toLatin1(), QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
if (hashEncrypted.size() <= key128Size || (hashEncrypted.size() % 0x10) != 0) { if (hashEncrypted.size() <= key128Size || (hashEncrypted.size() != key128Size + 0x20)) {
Ui::show(Box<InformBox>(tr::lng_confirm_phone_link_invalid(tr::now))); Ui::show(Box<InformBox>(tr::lng_confirm_phone_link_invalid(tr::now)));
return; return;
} }
@ -1350,37 +1350,46 @@ void ShareGameScoreByHash(
char dataSha1[20] = { 0 }; char dataSha1[20] = { 0 };
hashSha1(hashData.constData(), hashData.size(), dataSha1); hashSha1(hashData.constData(), hashData.size(), dataSha1);
// Mix out channel access hash from the first 64 bits of SHA1 of data. //// Mix out channel access hash from the first 64 bits of SHA1 of data.
auto channelAccessHash = *reinterpret_cast<uint64*>(hashEncrypted.data()) ^ *reinterpret_cast<uint64*>(dataSha1); //auto channelAccessHash = *reinterpret_cast<uint64*>(hashEncrypted.data()) ^ *reinterpret_cast<uint64*>(dataSha1);
// Check next 64 bits of SHA1() of data. //// Check next 64 bits of SHA1() of data.
auto skipSha1Part = sizeof(channelAccessHash); //auto skipSha1Part = sizeof(channelAccessHash);
if (memcmp(dataSha1 + skipSha1Part, hashEncrypted.constData() + skipSha1Part, key128Size - skipSha1Part) != 0) { //if (memcmp(dataSha1 + skipSha1Part, hashEncrypted.constData() + skipSha1Part, key128Size - skipSha1Part) != 0) {
// Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now)));
// return;
//}
// Check 128 bits of SHA1() of data.
if (memcmp(dataSha1, hashEncrypted.constData(), key128Size) != 0) {
Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now))); Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now)));
return; return;
} }
auto hashDataInts = reinterpret_cast<int32*>(hashData.data()); auto hashDataInts = reinterpret_cast<uint64*>(hashData.data());
if (hashDataInts[0] != session->userId()) { if (hashDataInts[0] != session->userId().bare) {
Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now))); Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now)));
return; return;
} }
// Check first 32 bits of channel access hash. // Check first 32 bits of channel access hash.
auto channelAccessHashInts = reinterpret_cast<int32*>(&channelAccessHash); auto channelAccessHash = hashDataInts[3];
if (channelAccessHashInts[0] != hashDataInts[3]) { //auto channelAccessHashInts = reinterpret_cast<int32*>(&channelAccessHash);
Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now))); //if (channelAccessHashInts[0] != hashDataInts[3]) {
return; // Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now)));
} // return;
//}
auto channelId = hashDataInts[1]; if (((hashDataInts[1] >> 40) != 0)
auto msgId = hashDataInts[2]; || ((hashDataInts[2] >> 32) != 0)
if (!channelId && channelAccessHash) { || (!hashDataInts[1] && channelAccessHash)) {
// If there is no channel id, there should be no channel access_hash. // If there is no channel id, there should be no channel access_hash.
Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now))); Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now)));
return; return;
} }
auto channelId = ChannelId(hashDataInts[1]);
auto msgId = MsgId(hashDataInts[2]);
if (const auto item = session->data().message(channelId, msgId)) { if (const auto item = session->data().message(channelId, msgId)) {
FastShareMessage(item); FastShareMessage(item);
} else { } else {
@ -1406,7 +1415,7 @@ void ShareGameScoreByHash(
MTP_vector<MTPInputChannel>( MTP_vector<MTPInputChannel>(
1, 1,
MTP_inputChannel( MTP_inputChannel(
MTP_int(channelId), MTP_int(channelId.bare), // #TODO ids
MTP_long(channelAccessHash))) MTP_long(channelAccessHash)))
)).done([=](const MTPmessages_Chats &result) { )).done([=](const MTPmessages_Chats &result) {
result.match([&](const auto &data) { result.match([&](const auto &data) {

View file

@ -393,8 +393,8 @@ callErrorToast: Toast(defaultToast) {
groupCallWidth: 380px; groupCallWidth: 380px;
groupCallHeight: 580px; groupCallHeight: 580px;
groupCallMuteButtonIconSize: size(55px, 55px); groupCallMuteButtonIconSize: size(67px, 67px);
groupCallMuteButtonIconTop: 42px; groupCallMuteButtonIconTop: 35px;
groupCallRipple: RippleAnimation(defaultRippleAnimation) { groupCallRipple: RippleAnimation(defaultRippleAnimation) {
color: groupCallMembersBgRipple; color: groupCallMembersBgRipple;
} }
@ -685,13 +685,19 @@ groupCallMemberInvited: icon {{ "calls/group_calls_invited", groupCallMemberInac
groupCallMemberInvitedPosition: point(2px, 12px); groupCallMemberInvitedPosition: point(2px, 12px);
groupCallMemberRaisedHand: icon {{ "calls/group_calls_raised_hand", groupCallMemberInactiveStatus }}; groupCallMemberRaisedHand: icon {{ "calls/group_calls_raised_hand", groupCallMemberInactiveStatus }};
groupCallSettingsInner: IconButton(callButton) {
iconPosition: point(-1px, 22px);
icon: icon {{ "calls/call_settings", groupCallIconFg }};
ripple: RippleAnimation(defaultRippleAnimation) {
color: callMuteRipple;
}
}
groupCallSettings: CallButton(callMicrophoneMute) { groupCallSettings: CallButton(callMicrophoneMute) {
button: IconButton(callButton) { button: groupCallSettingsInner;
iconPosition: point(-1px, 22px); }
icon: icon {{ "calls/call_settings", groupCallIconFg }}; groupCallShare: CallButton(groupCallSettings) {
ripple: RippleAnimation(defaultRippleAnimation) { button: IconButton(groupCallSettingsInner) {
color: callMuteRipple; icon: icon {{ "calls/group_calls_share", groupCallIconFg }};
}
} }
} }
groupCallHangup: CallButton(callHangup) { groupCallHangup: CallButton(callHangup) {
@ -720,6 +726,11 @@ groupCallTopBarJoin: RoundButton(defaultActiveButton) {
height: 26px; height: 26px;
textTop: 4px; textTop: 4px;
} }
groupCallTopBarOpen: RoundButton(groupCallTopBarJoin) {
ripple: RippleAnimation(defaultRippleAnimation) {
color: shadowFg;
}
}
groupCallBox: Box(defaultBox) { groupCallBox: Box(defaultBox) {
button: RoundButton(defaultBoxButton) { button: RoundButton(defaultBoxButton) {
textFg: groupCallActiveFg; textFg: groupCallActiveFg;
@ -940,3 +951,18 @@ callTopBarMuteCrossLine: CrossLineAnimation {
endPosition: point(26px, 23px); endPosition: point(26px, 23px);
stroke: 2px; stroke: 2px;
} }
groupCallStartsIn: FlatLabel(defaultFlatLabel) {
style: TextStyle(defaultTextStyle) {
font: font(20px semibold);
linkFont: font(20px semibold);
linkFontOver: font(20px semibold underline);
}
textFg: groupCallMembersFg;
}
groupCallScheduledBodyHeight: 200px;
groupCallStartsWhen: groupCallStartsIn;
groupCallStartsInTop: 10px;
groupCallStartsWhenTop: 160px;
groupCallCountdownFont: font(64px semibold);
groupCallCountdownTop: 52px;

View file

@ -409,7 +409,8 @@ void BoxController::receivedCalls(const QVector<MTPMessage> &result) {
NewMessageType::Existing); NewMessageType::Existing);
insertRow(item, InsertWay::Append); insertRow(item, InsertWay::Append);
} else { } else {
LOG(("API Error: a search results with not loaded peer %1").arg(peerId)); LOG(("API Error: a search results with not loaded peer %1"
).arg(peerId.value));
} }
_offsetId = msgId; _offsetId = msgId;
} }

View file

@ -490,13 +490,13 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
auto &data = call.c_phoneCallRequested(); auto &data = call.c_phoneCallRequested();
if (_type != Type::Incoming if (_type != Type::Incoming
|| _id != 0 || _id != 0
|| peerToUser(_user->id) != data.vadmin_id().v) { || peerToUser(_user->id) != UserId(data.vadmin_id())) {
Unexpected("phoneCallRequested call inside an existing call handleUpdate()"); Unexpected("phoneCallRequested call inside an existing call handleUpdate()");
} }
if (_user->session().userId() != data.vparticipant_id().v) { if (_user->session().userId() != UserId(data.vparticipant_id())) {
LOG(("Call Error: Wrong call participant_id %1, expected %2." LOG(("Call Error: Wrong call participant_id %1, expected %2."
).arg(data.vparticipant_id().v ).arg(data.vparticipant_id().v
).arg(_user->session().userId())); ).arg(_user->session().userId().bare));
finish(FinishType::Failed); finish(FinishType::Failed);
return true; return true;
} }
@ -891,12 +891,12 @@ bool Call::checkCallCommonFields(const T &call) {
} }
auto adminId = (_type == Type::Outgoing) ? _user->session().userId() : peerToUser(_user->id); auto adminId = (_type == Type::Outgoing) ? _user->session().userId() : peerToUser(_user->id);
auto participantId = (_type == Type::Outgoing) ? peerToUser(_user->id) : _user->session().userId(); auto participantId = (_type == Type::Outgoing) ? peerToUser(_user->id) : _user->session().userId();
if (call.vadmin_id().v != adminId) { if (UserId(call.vadmin_id()) != adminId) {
LOG(("Call Error: Wrong call admin_id %1, expected %2.").arg(call.vadmin_id().v).arg(adminId)); LOG(("Call Error: Wrong call admin_id %1, expected %2.").arg(call.vadmin_id().v).arg(adminId.bare));
return checkFailed(); return checkFailed();
} }
if (call.vparticipant_id().v != participantId) { if (UserId(call.vparticipant_id()) != participantId) {
LOG(("Call Error: Wrong call participant_id %1, expected %2.").arg(call.vparticipant_id().v).arg(participantId)); LOG(("Call Error: Wrong call participant_id %1, expected %2.").arg(call.vparticipant_id().v).arg(participantId.bare));
return checkFailed(); return checkFailed();
} }
return true; return true;

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_choose_join_as.h" #include "calls/calls_choose_join_as.h"
#include "calls/calls_group_common.h" #include "calls/calls_group_common.h"
#include "calls/calls_group_menu.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_channel.h" #include "data/data_channel.h"
@ -18,15 +19,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
#include "ui/boxes/choose_date_time.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "boxes/peer_list_box.h" #include "boxes/peer_list_box.h"
#include "boxes/confirm_box.h" #include "base/unixtime.h"
#include "base/timer_rpl.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_layers.h"
#include "styles/style_calls.h" #include "styles/style_calls.h"
namespace Calls::Group { namespace Calls::Group {
namespace { namespace {
constexpr auto kDefaultScheduleDuration = 60 * TimeId(60);
constexpr auto kLabelRefreshInterval = 10 * crl::time(1000);
using Context = ChooseJoinAsProcess::Context; using Context = ChooseJoinAsProcess::Context;
class ListController : public PeerListController { class ListController : public PeerListController {
@ -98,7 +105,7 @@ void ListController::rowClicked(not_null<PeerListRow*> row) {
if (peer == _selected) { if (peer == _selected) {
return; return;
} }
const auto previous = delegate()->peerListFindRow(_selected->id); const auto previous = delegate()->peerListFindRow(_selected->id.value);
Assert(previous != nullptr); Assert(previous != nullptr);
delegate()->peerListSetRowChecked(previous, false); delegate()->peerListSetRowChecked(previous, false);
delegate()->peerListSetRowChecked(row, true); delegate()->peerListSetRowChecked(row, true);
@ -109,6 +116,79 @@ not_null<PeerData*> ListController::selected() const {
return _selected; return _selected;
} }
void ScheduleGroupCallBox(
not_null<Ui::GenericBox*> box,
const JoinInfo &info,
Fn<void(JoinInfo)> done) {
const auto send = [=](TimeId date) {
box->closeBox();
auto copy = info;
copy.scheduleDate = date;
done(std::move(copy));
};
const auto duration = box->lifetime().make_state<
rpl::variable<QString>>();
auto description = (info.peer->isBroadcast()
? tr::lng_group_call_schedule_notified_channel
: tr::lng_group_call_schedule_notified_group)(
lt_duration,
duration->value());
const auto now = QDateTime::currentDateTime();
const auto min = [] {
return base::unixtime::serialize(
QDateTime::currentDateTime().addSecs(12));
};
const auto max = [] {
return base::unixtime::serialize(
QDateTime(QDate::currentDate().addDays(8), QTime(0, 0))) - 1;
};
// At least half an hour later, at zero minutes/seconds.
const auto schedule = QDateTime(
now.date(),
QTime(now.time().hour(), 0)
).addSecs(60 * 60 * (now.time().minute() < 30 ? 1 : 2));
auto descriptor = Ui::ChooseDateTimeBox(box, {
.title = tr::lng_group_call_schedule_title(),
.submit = tr::lng_schedule_button(),
.done = send,
.min = min,
.time = base::unixtime::serialize(schedule),
.max = max,
.description = std::move(description),
});
using namespace rpl::mappers;
*duration = rpl::combine(
rpl::single(
rpl::empty_value()
) | rpl::then(base::timer_each(kLabelRefreshInterval)),
std::move(descriptor.values) | rpl::filter(_1 != 0),
_2
) | rpl::map([](TimeId date) {
const auto now = base::unixtime::now();
const auto duration = (date - now);
if (duration >= 24 * 60 * 60) {
return tr::lng_group_call_duration_days(
tr::now,
lt_count,
duration / (24 * 60 * 60));
} else if (duration >= 60 * 60) {
return tr::lng_group_call_duration_hours(
tr::now,
lt_count,
duration / (60 * 60));
}
return tr::lng_group_call_duration_minutes(
tr::now,
lt_count,
std::max(duration / 60, 1));
});
}
void ChooseJoinAsBox( void ChooseJoinAsBox(
not_null<Ui::GenericBox*> box, not_null<Ui::GenericBox*> box,
Context context, Context context,
@ -124,12 +204,13 @@ void ChooseJoinAsBox(
} }
Unexpected("Context in ChooseJoinAsBox."); Unexpected("Context in ChooseJoinAsBox.");
}()); }());
const auto &labelSt = (context == Context::Switch)
? st::groupCallJoinAsLabel
: st::confirmPhoneAboutLabel;
box->addRow(object_ptr<Ui::FlatLabel>( box->addRow(object_ptr<Ui::FlatLabel>(
box, box,
tr::lng_group_call_join_as_about(), tr::lng_group_call_join_as_about(),
(context == Context::Switch labelSt));
? st::groupCallJoinAsLabel
: st::confirmPhoneAboutLabel)));
auto &lifetime = box->lifetime(); auto &lifetime = box->lifetime();
const auto delegate = lifetime.make_state< const auto delegate = lifetime.make_state<
@ -155,6 +236,25 @@ void ChooseJoinAsBox(
auto next = (context == Context::Switch) auto next = (context == Context::Switch)
? tr::lng_settings_save() ? tr::lng_settings_save()
: tr::lng_continue(); : tr::lng_continue();
if (context == Context::Create) {
const auto makeLink = [](const QString &text) {
return Ui::Text::Link(text);
};
const auto label = box->addRow(object_ptr<Ui::FlatLabel>(
box,
tr::lng_group_call_or_schedule(
lt_link,
tr::lng_group_call_schedule(makeLink),
Ui::Text::WithEntities),
labelSt));
label->setClickHandlerFilter([=](const auto&...) {
auto withJoinAs = info;
withJoinAs.joinAs = controller->selected();
box->getDelegate()->show(
Box(ScheduleGroupCallBox, withJoinAs, done));
return false;
});
}
box->addButton(std::move(next), [=] { box->addButton(std::move(next), [=] {
auto copy = info; auto copy = info;
copy.joinAs = controller->selected(); copy.joinAs = controller->selected();
@ -296,7 +396,7 @@ void ChooseJoinAsProcess::start(
&& (peer->groupCall() != nullptr); && (peer->groupCall() != nullptr);
if (!changingJoinAsFrom && (onlyByMe || byAlreadyUsed)) { if (!changingJoinAsFrom && (onlyByMe || byAlreadyUsed)) {
const auto confirmation = CreateOrJoinConfirmation( auto confirmation = CreateOrJoinConfirmation(
peer, peer,
context, context,
byAlreadyUsed); byAlreadyUsed);
@ -304,12 +404,36 @@ void ChooseJoinAsProcess::start(
finish(info); finish(info);
return; return;
} }
auto box = Box<::ConfirmBox>( const auto creating = !peer->groupCall();
confirmation, if (creating) {
(peer->groupCall() confirmation
? tr::lng_group_call_join(tr::now) .append("\n\n")
: tr::lng_create_group_create(tr::now)), .append(tr::lng_group_call_or_schedule(
crl::guard(&_request->guard, [=] { finish(info); })); tr::now,
lt_link,
Ui::Text::Link(tr::lng_group_call_schedule(tr::now)),
Ui::Text::WithEntities));
}
const auto guard = base::make_weak(&_request->guard);
const auto safeFinish = crl::guard(guard, [=] { finish(info); });
const auto filter = [=](const auto &...) {
if (guard) {
_request->showBox(Box(
ScheduleGroupCallBox,
info,
crl::guard(guard, finish)));
}
return false;
};
auto box = ConfirmBox({
.text = confirmation,
.button = (creating
? tr::lng_create_group_create()
: tr::lng_group_call_join()),
.callback = crl::guard(guard, [=] { finish(info); }),
.st = &st::boxLabel,
.filter = filter,
});
box->boxClosing( box->boxClosing(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
_request = nullptr; _request = nullptr;

View file

@ -112,7 +112,7 @@ private:
} }
if (const auto chat = peer->asChat()) { if (const auto chat = peer->asChat()) {
return chat->admins.contains(user) return chat->admins.contains(user)
|| (chat->creator == user->bareId()); || (chat->creator == peerToUser(user->id));
} else if (const auto group = peer->asChannel()) { } else if (const auto group = peer->asChannel()) {
if (const auto mgInfo = group->mgInfo.get()) { if (const auto mgInfo = group->mgInfo.get()) {
if (mgInfo->creator == user) { if (mgInfo->creator == user) {
@ -184,6 +184,8 @@ GroupCall::GroupCall(
, _joinAs(info.joinAs) , _joinAs(info.joinAs)
, _possibleJoinAs(std::move(info.possibleJoinAs)) , _possibleJoinAs(std::move(info.possibleJoinAs))
, _joinHash(info.joinHash) , _joinHash(info.joinHash)
, _id(inputCall.c_inputGroupCall().vid().v)
, _scheduleDate(info.scheduleDate)
, _lastSpokeCheckTimer([=] { checkLastSpoke(); }) , _lastSpokeCheckTimer([=] { checkLastSpoke(); })
, _checkJoinedTimer([=] { checkJoined(); }) , _checkJoinedTimer([=] { checkJoined(); })
, _pushToTalkCancelTimer([=] { pushToTalkCancel(); }) , _pushToTalkCancelTimer([=] { pushToTalkCancel(); })
@ -215,17 +217,35 @@ GroupCall::GroupCall(
checkGlobalShortcutAvailability(); checkGlobalShortcutAvailability();
const auto id = inputCall.c_inputGroupCall().vid().v; if (const auto real = lookupReal()) {
if (id) { subscribeToReal(real);
if (const auto call = _peer->groupCall(); call && call->id() == id) { if (!_peer->canManageGroupCall() && real->joinMuted()) {
if (!_peer->canManageGroupCall() && call->joinMuted()) { _muted = MuteState::ForceMuted;
_muted = MuteState::ForceMuted;
}
} }
_state = State::Joining; } else {
_peer->session().changes().peerFlagsValue(
_peer,
Data::PeerUpdate::Flag::GroupCall
) | rpl::map([=] {
return lookupReal();
}) | rpl::filter([](Data::GroupCall *real) {
return real != nullptr;
}) | rpl::map([](Data::GroupCall *real) {
return not_null{ real };
}) | rpl::take(
1
) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) {
subscribeToReal(real);
_realChanges.fire_copy(real);
}, _lifetime);
}
if (_id) {
join(inputCall); join(inputCall);
} else { } else {
start(); start(info.scheduleDate);
}
if (_scheduleDate) {
saveDefaultJoinAs(_joinAs);
} }
_mediaDevices->audioInputId( _mediaDevices->audioInputId(
@ -249,6 +269,17 @@ GroupCall::~GroupCall() {
destroyController(); destroyController();
} }
void GroupCall::subscribeToReal(not_null<Data::GroupCall*> real) {
real->scheduleDateValue(
) | rpl::start_with_next([=](TimeId date) {
const auto was = _scheduleDate;
_scheduleDate = date;
if (was && !date) {
join(inputCall());
}
}, _lifetime);
}
void GroupCall::checkGlobalShortcutAvailability() { void GroupCall::checkGlobalShortcutAvailability() {
auto &settings = Core::App().settings(); auto &settings = Core::App().settings();
if (!settings.groupCallPushToTalk()) { if (!settings.groupCallPushToTalk()) {
@ -326,10 +357,33 @@ bool GroupCall::showChooseJoinAs() const {
&& !_possibleJoinAs.front()->isSelf()); && !_possibleJoinAs.front()->isSelf());
} }
void GroupCall::start() { bool GroupCall::scheduleStartSubscribed() const {
if (const auto real = lookupReal()) {
return real->scheduleStartSubscribed();
}
return false;
}
Data::GroupCall *GroupCall::lookupReal() const {
const auto real = _peer->groupCall();
return (real && real->id() == _id) ? real : nullptr;
}
rpl::producer<not_null<Data::GroupCall*>> GroupCall::real() const {
if (const auto real = lookupReal()) {
return rpl::single(not_null{ real });
}
return _realChanges.events();
}
void GroupCall::start(TimeId scheduleDate) {
using Flag = MTPphone_CreateGroupCall::Flag;
_createRequestId = _api.request(MTPphone_CreateGroupCall( _createRequestId = _api.request(MTPphone_CreateGroupCall(
MTP_flags(scheduleDate ? Flag::f_schedule_date : Flag(0)),
_peer->input, _peer->input,
MTP_int(openssl::RandomValue<int32>()) MTP_int(openssl::RandomValue<int32>()),
MTPstring(), // title
MTP_int(scheduleDate)
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
_acceptFields = true; _acceptFields = true;
_peer->session().api().applyUpdates(result); _peer->session().api().applyUpdates(result);
@ -347,20 +401,16 @@ void GroupCall::start() {
} }
void GroupCall::join(const MTPInputGroupCall &inputCall) { void GroupCall::join(const MTPInputGroupCall &inputCall) {
setState(State::Joining);
if (const auto chat = _peer->asChat()) {
chat->setGroupCall(inputCall);
} else if (const auto group = _peer->asChannel()) {
group->setGroupCall(inputCall);
} else {
Unexpected("Peer type in GroupCall::join.");
}
inputCall.match([&](const MTPDinputGroupCall &data) { inputCall.match([&](const MTPDinputGroupCall &data) {
_id = data.vid().v; _id = data.vid().v;
_accessHash = data.vaccess_hash().v; _accessHash = data.vaccess_hash().v;
rejoin();
}); });
setState(_scheduleDate ? State::Waiting : State::Joining);
if (_scheduleDate) {
return;
}
rejoin();
using Update = Data::GroupCall::ParticipantUpdate; using Update = Data::GroupCall::ParticipantUpdate;
_peer->groupCall()->participantUpdated( _peer->groupCall()->participantUpdated(
@ -409,6 +459,23 @@ void GroupCall::rejoinWithHash(const QString &hash) {
} }
} }
void GroupCall::setJoinAs(not_null<PeerData*> as) {
_joinAs = as;
if (const auto chat = _peer->asChat()) {
chat->setGroupCallDefaultJoinAs(_joinAs->id);
} else if (const auto channel = _peer->asChannel()) {
channel->setGroupCallDefaultJoinAs(_joinAs->id);
}
}
void GroupCall::saveDefaultJoinAs(not_null<PeerData*> as) {
setJoinAs(as);
_api.request(MTPphone_SaveDefaultGroupCallJoinAs(
_peer->input,
_joinAs->input
)).send();
}
void GroupCall::rejoin(not_null<PeerData*> as) { void GroupCall::rejoin(not_null<PeerData*> as) {
if (state() != State::Joining if (state() != State::Joining
&& state() != State::Joined && state() != State::Joined
@ -424,12 +491,7 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
applyMeInCallLocally(); applyMeInCallLocally();
LOG(("Call Info: Requesting join payload.")); LOG(("Call Info: Requesting join payload."));
_joinAs = as; setJoinAs(as);
if (const auto chat = _peer->asChat()) {
chat->setGroupCallDefaultJoinAs(_joinAs->id);
} else if (const auto channel = _peer->asChannel()) {
channel->setGroupCallDefaultJoinAs(_joinAs->id);
}
const auto weak = base::make_weak(this); const auto weak = base::make_weak(this);
_instance->emitJoinPayload([=](tgcalls::GroupJoinPayload payload) { _instance->emitJoinPayload([=](tgcalls::GroupJoinPayload payload) {
@ -479,6 +541,7 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
applyMeInCallLocally(); applyMeInCallLocally();
maybeSendMutedUpdate(wasMuteState); maybeSendMutedUpdate(wasMuteState);
_peer->session().api().applyUpdates(updates); _peer->session().api().applyUpdates(updates);
applyQueuedSelfUpdates();
checkFirstTimeJoined(); checkFirstTimeJoined();
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {
const auto type = error.type(); const auto type = error.type();
@ -561,7 +624,8 @@ void GroupCall::applyMeInCallLocally() {
MTP_int(_mySsrc), MTP_int(_mySsrc),
MTP_int(volume), MTP_int(volume),
MTPstring(), // Don't update about text in local updates. MTPstring(), // Don't update about text in local updates.
MTP_long(raisedHandRating))), MTP_long(raisedHandRating),
MTPDataJSON())),
MTP_int(0)).c_updateGroupCallParticipants()); MTP_int(0)).c_updateGroupCallParticipants());
} }
@ -606,7 +670,8 @@ void GroupCall::applyParticipantLocally(
MTP_int(participant->ssrc), MTP_int(participant->ssrc),
MTP_int(volume.value_or(participant->volume)), MTP_int(volume.value_or(participant->volume)),
MTPstring(), // Don't update about text in local updates. MTPstring(), // Don't update about text in local updates.
MTP_long(participant->raisedHandRating))), MTP_long(participant->raisedHandRating),
MTPDataJSON())),
MTP_int(0)).c_updateGroupCallParticipants()); MTP_int(0)).c_updateGroupCallParticipants());
} }
@ -641,8 +706,12 @@ void GroupCall::rejoinAs(Group::JoinInfo info) {
.wasJoinAs = _joinAs, .wasJoinAs = _joinAs,
.nowJoinAs = info.joinAs, .nowJoinAs = info.joinAs,
}; };
setState(State::Joining); if (_scheduleDate) {
rejoin(info.joinAs); saveDefaultJoinAs(info.joinAs);
} else {
setState(State::Joining);
rejoin(info.joinAs);
}
_rejoinEvents.fire_copy(event); _rejoinEvents.fire_copy(event);
} }
@ -686,6 +755,29 @@ void GroupCall::finish(FinishType type) {
})).send(); })).send();
} }
void GroupCall::startScheduledNow() {
if (!lookupReal()) {
return;
}
_api.request(MTPphone_StartScheduledGroupCall(
inputCall()
)).done([=](const MTPUpdates &result) {
_peer->session().api().applyUpdates(result);
}).send();
}
void GroupCall::toggleScheduleStartSubscribed(bool subscribed) {
if (!lookupReal()) {
return;
}
_api.request(MTPphone_ToggleGroupCallStartSubscription(
inputCall(),
MTP_bool(subscribed)
)).done([=](const MTPUpdates &result) {
_peer->session().api().applyUpdates(result);
}).send();
}
void GroupCall::setMuted(MuteState mute) { void GroupCall::setMuted(MuteState mute) {
const auto set = [=] { const auto set = [=] {
const auto wasMuted = (muted() == MuteState::Muted) const auto wasMuted = (muted() == MuteState::Muted)
@ -729,9 +821,25 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
void GroupCall::handlePossibleCreateOrJoinResponse( void GroupCall::handlePossibleCreateOrJoinResponse(
const MTPDgroupCall &data) { const MTPDgroupCall &data) {
if (const auto date = data.vschedule_date()) {
_scheduleDate = date->v;
} else {
_scheduleDate = 0;
}
if (_acceptFields) { if (_acceptFields) {
if (!_instance && !_id) { if (!_instance && !_id) {
join(MTP_inputGroupCall(data.vid(), data.vaccess_hash())); const auto input = MTP_inputGroupCall(
data.vid(),
data.vaccess_hash());
const auto scheduleDate = data.vschedule_date().value_or_empty();
if (const auto chat = _peer->asChat()) {
chat->setGroupCall(input, scheduleDate);
} else if (const auto group = _peer->asChannel()) {
group->setGroupCall(input, scheduleDate);
} else {
Unexpected("Peer type in GroupCall::join.");
}
join(input);
} }
return; return;
} else if (_id != data.vid().v || !_instance) { } else if (_id != data.vid().v || !_instance) {
@ -825,10 +933,8 @@ void GroupCall::handlePossibleDiscarded(const MTPDgroupCallDiscarded &data) {
} }
void GroupCall::addParticipantsToInstance() { void GroupCall::addParticipantsToInstance() {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real if (!real || (_instanceMode == InstanceMode::None)) {
|| (real->id() != _id)
|| (_instanceMode == InstanceMode::None)) {
return; return;
} }
for (const auto &participant : real->participants()) { for (const auto &participant : real->participants()) {
@ -850,7 +956,7 @@ void GroupCall::addPreparedParticipants() {
if (!_preparedParticipants.empty()) { if (!_preparedParticipants.empty()) {
_instance->addParticipants(base::take(_preparedParticipants)); _instance->addParticipants(base::take(_preparedParticipants));
} }
if (const auto real = _peer->groupCall(); real && real->id() == _id) { if (const auto real = lookupReal()) {
if (!_unresolvedSsrcs.empty()) { if (!_unresolvedSsrcs.empty()) {
real->resolveParticipants(base::take(_unresolvedSsrcs)); real->resolveParticipants(base::take(_unresolvedSsrcs));
} }
@ -890,91 +996,105 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
return; return;
} }
const auto state = _state.current(); const auto state = _state.current();
if (state != State::Joined && state != State::Connecting) { const auto joined = (state == State::Joined)
return; || (state == State::Connecting);
}
const auto handleOtherParticipants = [=](
const MTPDgroupCallParticipant &data) {
if (data.is_min()) {
// No real information about mutedByMe or my custom volume.
return;
}
const auto participantPeer = _peer->owner().peer(
peerFromMTP(data.vpeer()));
const auto participant = LookupParticipant(
_peer,
_id,
participantPeer);
if (!participant) {
return;
}
_otherParticipantStateValue.fire(Group::ParticipantState{
.peer = participantPeer,
.volume = data.vvolume().value_or_empty(),
.mutedByMe = data.is_muted_by_you(),
});
};
for (const auto &participant : data.vparticipants().v) { for (const auto &participant : data.vparticipants().v) {
participant.match([&](const MTPDgroupCallParticipant &data) { participant.match([&](const MTPDgroupCallParticipant &data) {
const auto isSelf = data.is_self() const auto isSelf = data.is_self()
|| (data.is_min() || (data.is_min()
&& peerFromMTP(data.vpeer()) == _joinAs->id); && peerFromMTP(data.vpeer()) == _joinAs->id);
if (!isSelf) { if (!isSelf) {
handleOtherParticipants(data); applyOtherParticipantUpdate(data);
return; } else if (joined) {
} applySelfUpdate(data);
if (data.is_left()) { } else {
if (data.vsource().v == _mySsrc) { _queuedSelfUpdates.push_back(participant);
// I was removed from the call, rejoin.
LOG(("Call Info: "
"Rejoin after got 'left' with my ssrc."));
setState(State::Joining);
rejoin();
}
return;
} else if (data.vsource().v != _mySsrc) {
if (!_mySsrcs.contains(data.vsource().v)) {
// I joined from another device, hangup.
LOG(("Call Info: "
"Hangup after '!left' with ssrc %1, my %2."
).arg(data.vsource().v
).arg(_mySsrc));
_mySsrc = 0;
hangup();
} else {
LOG(("Call Info: "
"Some old 'self' with '!left' and ssrc %1, my %2."
).arg(data.vsource().v
).arg(_mySsrc));
}
return;
}
if (data.is_muted() && !data.is_can_self_unmute()) {
setMuted(data.vraise_hand_rating().value_or_empty()
? MuteState::RaisedHand
: MuteState::ForceMuted);
} else if (_instanceMode == InstanceMode::Stream) {
LOG(("Call Info: Rejoin after unforcemute in stream mode."));
setState(State::Joining);
rejoin();
} else if (muted() == MuteState::ForceMuted
|| muted() == MuteState::RaisedHand) {
setMuted(MuteState::Muted);
if (!_instanceTransitioning) {
notifyAboutAllowedToSpeak();
}
} else if (data.is_muted() && muted() != MuteState::Muted) {
setMuted(MuteState::Muted);
} }
}); });
} }
} }
void GroupCall::applyQueuedSelfUpdates() {
const auto weak = base::make_weak(this);
while (weak
&& !_queuedSelfUpdates.empty()
&& (_state.current() == State::Joined
|| _state.current() == State::Connecting)) {
const auto update = _queuedSelfUpdates.front();
_queuedSelfUpdates.erase(_queuedSelfUpdates.begin());
update.match([&](const MTPDgroupCallParticipant &data) {
applySelfUpdate(data);
});
}
}
void GroupCall::applySelfUpdate(const MTPDgroupCallParticipant &data) {
if (data.is_left()) {
if (data.vsource().v == _mySsrc) {
// I was removed from the call, rejoin.
LOG(("Call Info: "
"Rejoin after got 'left' with my ssrc."));
setState(State::Joining);
rejoin();
}
return;
} else if (data.vsource().v != _mySsrc) {
if (!_mySsrcs.contains(data.vsource().v)) {
// I joined from another device, hangup.
LOG(("Call Info: "
"Hangup after '!left' with ssrc %1, my %2."
).arg(data.vsource().v
).arg(_mySsrc));
_mySsrc = 0;
hangup();
} else {
LOG(("Call Info: "
"Some old 'self' with '!left' and ssrc %1, my %2."
).arg(data.vsource().v
).arg(_mySsrc));
}
return;
}
if (data.is_muted() && !data.is_can_self_unmute()) {
setMuted(data.vraise_hand_rating().value_or_empty()
? MuteState::RaisedHand
: MuteState::ForceMuted);
} else if (_instanceMode == InstanceMode::Stream) {
LOG(("Call Info: Rejoin after unforcemute in stream mode."));
setState(State::Joining);
rejoin();
} else if (muted() == MuteState::ForceMuted
|| muted() == MuteState::RaisedHand) {
setMuted(MuteState::Muted);
if (!_instanceTransitioning) {
notifyAboutAllowedToSpeak();
}
} else if (data.is_muted() && muted() != MuteState::Muted) {
setMuted(MuteState::Muted);
}
}
void GroupCall::applyOtherParticipantUpdate(
const MTPDgroupCallParticipant &data) {
if (data.is_min()) {
// No real information about mutedByMe or my custom volume.
return;
}
const auto participantPeer = _peer->owner().peer(
peerFromMTP(data.vpeer()));
if (!LookupParticipant(_peer, _id, participantPeer)) {
return;
}
_otherParticipantStateValue.fire(Group::ParticipantState{
.peer = participantPeer,
.volume = data.vvolume().value_or_empty(),
.mutedByMe = data.is_muted_by_you(),
});
}
void GroupCall::changeTitle(const QString &title) { void GroupCall::changeTitle(const QString &title) {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real || real->id() != _id || real->title() == title) { if (!real || real->title() == title) {
return; return;
} }
@ -989,8 +1109,8 @@ void GroupCall::changeTitle(const QString &title) {
} }
void GroupCall::toggleRecording(bool enabled, const QString &title) { void GroupCall::toggleRecording(bool enabled, const QString &title) {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real || real->id() != _id) { if (!real) {
return; return;
} }
@ -1169,10 +1289,8 @@ void GroupCall::broadcastPartCancel(not_null<LoadPartTask*> task) {
void GroupCall::requestParticipantsInformation( void GroupCall::requestParticipantsInformation(
const std::vector<uint32_t> &ssrcs) { const std::vector<uint32_t> &ssrcs) {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real if (!real || (_instanceMode == InstanceMode::None)) {
|| (real->id() != _id)
|| (_instanceMode == InstanceMode::None)) {
for (const auto ssrc : ssrcs) { for (const auto ssrc : ssrcs) {
_unresolvedSsrcs.emplace(ssrc); _unresolvedSsrcs.emplace(ssrc);
} }
@ -1206,8 +1324,8 @@ void GroupCall::updateInstanceMuteState() {
} }
void GroupCall::updateInstanceVolumes() { void GroupCall::updateInstanceVolumes() {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real || real->id() != _id) { if (!real) {
return; return;
} }
@ -1283,8 +1401,8 @@ void GroupCall::audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data) {
} }
void GroupCall::checkLastSpoke() { void GroupCall::checkLastSpoke() {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real || real->id() != _id) { if (!real) {
return; return;
} }
@ -1495,8 +1613,8 @@ void GroupCall::editParticipant(
std::variant<int, not_null<UserData*>> GroupCall::inviteUsers( std::variant<int, not_null<UserData*>> GroupCall::inviteUsers(
const std::vector<not_null<UserData*>> &users) { const std::vector<not_null<UserData*>> &users) {
const auto real = _peer->groupCall(); const auto real = lookupReal();
if (!real || real->id() != _id) { if (!real) {
return 0; return 0;
} }
const auto owner = &_peer->owner(); const auto owner = &_peer->owner();

View file

@ -34,6 +34,7 @@ class MediaDevices;
namespace Data { namespace Data {
struct LastSpokeTimes; struct LastSpokeTimes;
struct GroupCallParticipant; struct GroupCallParticipant;
class GroupCall;
} // namespace Data } // namespace Data
namespace Calls { namespace Calls {
@ -109,8 +110,15 @@ public:
return _joinAs; return _joinAs;
} }
[[nodiscard]] bool showChooseJoinAs() const; [[nodiscard]] bool showChooseJoinAs() const;
[[nodiscard]] TimeId scheduleDate() const {
return _scheduleDate;
}
[[nodiscard]] bool scheduleStartSubscribed() const;
void start(); [[nodiscard]] Data::GroupCall *lookupReal() const;
[[nodiscard]] rpl::producer<not_null<Data::GroupCall*>> real() const;
void start(TimeId scheduleDate);
void hangup(); void hangup();
void discard(); void discard();
void rejoinAs(Group::JoinInfo info); void rejoinAs(Group::JoinInfo info);
@ -123,6 +131,8 @@ public:
[[nodiscard]] bool recordingStoppedByMe() const { [[nodiscard]] bool recordingStoppedByMe() const {
return _recordingStoppedByMe; return _recordingStoppedByMe;
} }
void startScheduledNow();
void toggleScheduleStartSubscribed(bool subscribed);
void setMuted(MuteState mute); void setMuted(MuteState mute);
void setMutedAndUpdate(MuteState mute); void setMutedAndUpdate(MuteState mute);
@ -138,6 +148,7 @@ public:
enum State { enum State {
Creating, Creating,
Waiting,
Joining, Joining,
Connecting, Connecting,
Joined, Joined,
@ -245,6 +256,9 @@ private:
void applyMeInCallLocally(); void applyMeInCallLocally();
void rejoin(); void rejoin();
void rejoin(not_null<PeerData*> as); void rejoin(not_null<PeerData*> as);
void setJoinAs(not_null<PeerData*> as);
void saveDefaultJoinAs(not_null<PeerData*> as);
void subscribeToReal(not_null<Data::GroupCall*> real);
void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data); void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data);
void setInstanceConnected(tgcalls::GroupNetworkState networkState); void setInstanceConnected(tgcalls::GroupNetworkState networkState);
@ -276,6 +290,9 @@ private:
not_null<PeerData*> participantPeer, not_null<PeerData*> participantPeer,
bool mute, bool mute,
std::optional<int> volume); std::optional<int> volume);
void applyQueuedSelfUpdates();
void applySelfUpdate(const MTPDgroupCallParticipant &data);
void applyOtherParticipantUpdate(const MTPDgroupCallParticipant &data);
[[nodiscard]] MTPInputGroupCall inputCall() const; [[nodiscard]] MTPInputGroupCall inputCall() const;
@ -284,6 +301,7 @@ private:
rpl::event_stream<PeerData*> _peerStream; rpl::event_stream<PeerData*> _peerStream;
not_null<History*> _history; // Can change in legacy group migration. not_null<History*> _history; // Can change in legacy group migration.
MTP::Sender _api; MTP::Sender _api;
rpl::event_stream<not_null<Data::GroupCall*>> _realChanges;
rpl::variable<State> _state = State::Creating; rpl::variable<State> _state = State::Creating;
rpl::variable<InstanceState> _instanceState rpl::variable<InstanceState> _instanceState
= InstanceState::Disconnected; = InstanceState::Disconnected;
@ -306,10 +324,12 @@ private:
bool _acceptFields = false; bool _acceptFields = false;
rpl::event_stream<Group::ParticipantState> _otherParticipantStateValue; rpl::event_stream<Group::ParticipantState> _otherParticipantStateValue;
std::vector<MTPGroupCallParticipant> _queuedSelfUpdates;
uint64 _id = 0; uint64 _id = 0;
uint64 _accessHash = 0; uint64 _accessHash = 0;
uint32 _mySsrc = 0; uint32 _mySsrc = 0;
TimeId _scheduleDate = 0;
base::flat_set<uint32> _mySsrcs; base::flat_set<uint32> _mySsrcs;
mtpRequestId _createRequestId = 0; mtpRequestId _createRequestId = 0;
mtpRequestId _updateMuteRequestId = 0; mtpRequestId _updateMuteRequestId = 0;

View file

@ -44,6 +44,7 @@ struct JoinInfo {
not_null<PeerData*> joinAs; not_null<PeerData*> joinAs;
std::vector<not_null<PeerData*>> possibleJoinAs; std::vector<not_null<PeerData*>> possibleJoinAs;
QString joinHash; QString joinHash;
TimeId scheduleDate = 0;
}; };
} // namespace Calls::Group } // namespace Calls::Group

View file

@ -317,7 +317,7 @@ private:
not_null<PeerData*> participantPeer, not_null<PeerData*> participantPeer,
bool participantIsCallAdmin, bool participantIsCallAdmin,
not_null<Row*> row); not_null<Row*> row);
void setupListChangeViewers(not_null<GroupCall*> call); void setupListChangeViewers();
void subscribeToChanges(not_null<Data::GroupCall*> real); void subscribeToChanges(not_null<Data::GroupCall*> real);
void updateRow( void updateRow(
const std::optional<Data::GroupCall::Participant> &was, const std::optional<Data::GroupCall::Participant> &was,
@ -335,16 +335,11 @@ private:
uint64 raiseHandRating) const; uint64 raiseHandRating) const;
Row *findRow(not_null<PeerData*> participantPeer) const; Row *findRow(not_null<PeerData*> participantPeer) const;
[[nodiscard]] Data::GroupCall *resolvedRealCall() const;
void appendInvitedUsers(); void appendInvitedUsers();
void scheduleRaisedHandStatusRemove(); void scheduleRaisedHandStatusRemove();
const base::weak_ptr<GroupCall> _call; const not_null<GroupCall*> _call;
not_null<PeerData*> _peer; not_null<PeerData*> _peer;
// Use only resolvedRealCall() method, not this value directly.
Data::GroupCall *_realCallRawValue = nullptr;
uint64 _realId = 0;
bool _prepared = false; bool _prepared = false;
rpl::event_stream<MuteRequest> _toggleMuteRequests; rpl::event_stream<MuteRequest> _toggleMuteRequests;
@ -909,7 +904,7 @@ MembersController::MembersController(
, _raisedHandStatusRemoveTimer([=] { scheduleRaisedHandStatusRemove(); }) , _raisedHandStatusRemoveTimer([=] { scheduleRaisedHandStatusRemove(); })
, _inactiveCrossLine(st::groupCallMemberInactiveCrossLine) , _inactiveCrossLine(st::groupCallMemberInactiveCrossLine)
, _coloredCrossLine(st::groupCallMemberColoredCrossLine) { , _coloredCrossLine(st::groupCallMemberColoredCrossLine) {
setupListChangeViewers(call); setupListChangeViewers();
style::PaletteChanged( style::PaletteChanged(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
@ -964,32 +959,20 @@ MembersController::~MembersController() {
base::take(_menu); base::take(_menu);
} }
void MembersController::setupListChangeViewers(not_null<GroupCall*> call) { void MembersController::setupListChangeViewers() {
const auto peer = call->peer(); _call->real(
peer->session().changes().peerFlagsValue(
peer,
Data::PeerUpdate::Flag::GroupCall
) | rpl::map([=] {
return peer->groupCall();
}) | rpl::filter([=](Data::GroupCall *real) {
const auto call = _call.get();
return call && real && (real->id() == call->id());
}) | rpl::take(
1
) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) { ) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) {
subscribeToChanges(real); subscribeToChanges(real);
}, _lifetime); }, _lifetime);
call->stateValue( _call->stateValue(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
const auto call = _call.get(); if (const auto real = _call->lookupReal()) {
const auto real = peer->groupCall();
if (call && real && (real->id() == call->id())) {
//updateRow(channel->session().user()); //updateRow(channel->session().user());
} }
}, _lifetime); }, _lifetime);
call->levelUpdates( _call->levelUpdates(
) | rpl::start_with_next([=](const LevelUpdate &update) { ) | rpl::start_with_next([=](const LevelUpdate &update) {
const auto i = _soundingRowBySsrc.find(update.ssrc); const auto i = _soundingRowBySsrc.find(update.ssrc);
if (i != end(_soundingRowBySsrc)) { if (i != end(_soundingRowBySsrc)) {
@ -997,7 +980,7 @@ void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
} }
}, _lifetime); }, _lifetime);
call->rejoinEvents( _call->rejoinEvents(
) | rpl::start_with_next([=](const Group::RejoinEvent &event) { ) | rpl::start_with_next([=](const Group::RejoinEvent &event) {
const auto guard = gsl::finally([&] { const auto guard = gsl::finally([&] {
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
@ -1014,9 +997,6 @@ void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
} }
void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) { void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
_realCallRawValue = real;
_realId = real->id();
_fullCount = real->fullCountValue(); _fullCount = real->fullCountValue();
real->participantsSliceAdded( real->participantsSliceAdded(
@ -1053,17 +1033,19 @@ void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
} }
void MembersController::appendInvitedUsers() { void MembersController::appendInvitedUsers() {
for (const auto user : _peer->owner().invitedToCallUsers(_realId)) { if (const auto id = _call->id()) {
if (auto row = createInvitedRow(user)) { for (const auto user : _peer->owner().invitedToCallUsers(id)) {
delegate()->peerListAppendRow(std::move(row)); if (auto row = createInvitedRow(user)) {
delegate()->peerListAppendRow(std::move(row));
}
} }
delegate()->peerListRefreshRows();
} }
delegate()->peerListRefreshRows();
using Invite = Data::Session::InviteToCall; using Invite = Data::Session::InviteToCall;
_peer->owner().invitesToCalls( _peer->owner().invitesToCalls(
) | rpl::filter([=](const Invite &invite) { ) | rpl::filter([=](const Invite &invite) {
return (invite.id == _realId); return (invite.id == _call->id());
}) | rpl::start_with_next([=](const Invite &invite) { }) | rpl::start_with_next([=](const Invite &invite) {
if (auto row = createInvitedRow(invite.user)) { if (auto row = createInvitedRow(invite.user)) {
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
@ -1120,7 +1102,7 @@ void MembersController::updateRow(
if (checkPosition) { if (checkPosition) {
checkRowPosition(checkPosition); checkRowPosition(checkPosition);
} else if (addedToBottom) { } else if (addedToBottom) {
const auto real = resolvedRealCall(); const auto real = _call->lookupReal();
if (real && real->joinedToTop()) { if (real && real->joinedToTop()) {
const auto proj = [&](const PeerListRow &other) { const auto proj = [&](const PeerListRow &other) {
const auto &real = static_cast<const Row&>(other); const auto &real = static_cast<const Row&>(other);
@ -1311,15 +1293,7 @@ void MembersController::updateRowLevel(
Row *MembersController::findRow(not_null<PeerData*> participantPeer) const { Row *MembersController::findRow(not_null<PeerData*> participantPeer) const {
return static_cast<Row*>( return static_cast<Row*>(
delegate()->peerListFindRow(participantPeer->id)); delegate()->peerListFindRow(participantPeer->id.value));
}
Data::GroupCall *MembersController::resolvedRealCall() const {
return (_realCallRawValue
&& (_peer->groupCall() == _realCallRawValue)
&& (_realCallRawValue->id() == _realId))
? _realCallRawValue
: nullptr;
} }
Main::Session &MembersController::session() const { Main::Session &MembersController::session() const {
@ -1332,9 +1306,7 @@ void MembersController::prepare() {
setDescriptionText(tr::lng_contacts_loading(tr::now)); setDescriptionText(tr::lng_contacts_loading(tr::now));
setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now)); setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
const auto call = _call.get(); if (const auto real = _call->lookupReal()) {
if (const auto real = _peer->groupCall()
; real && call && real->id() == call->id()) {
prepareRows(real); prepareRows(real);
} else if (auto row = createRowForMe()) { } else if (auto row = createRowForMe()) {
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
@ -1342,15 +1314,12 @@ void MembersController::prepare() {
} }
loadMoreRows(); loadMoreRows();
if (_realId) { appendInvitedUsers();
appendInvitedUsers();
}
_prepared = true; _prepared = true;
} }
bool MembersController::isMe(not_null<PeerData*> participantPeer) const { bool MembersController::isMe(not_null<PeerData*> participantPeer) const {
const auto call = _call.get(); return (_call->joinAs() == participantPeer);
return call && (call->joinAs() == participantPeer);
} }
void MembersController::prepareRows(not_null<Data::GroupCall*> real) { void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
@ -1379,19 +1348,17 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
} }
} }
if (!foundMe) { if (!foundMe) {
if (const auto call = _call.get()) { const auto me = _call->joinAs();
const auto me = call->joinAs(); const auto i = ranges::find(
const auto i = ranges::find( participants,
participants, me,
me, &Data::GroupCall::Participant::peer);
&Data::GroupCall::Participant::peer); auto row = (i != end(participants))
auto row = (i != end(participants)) ? createRow(*i)
? createRow(*i) : createRowForMe();
: createRowForMe(); if (row) {
if (row) { changed = true;
changed = true; delegate()->peerListAppendRow(std::move(row));
delegate()->peerListAppendRow(std::move(row));
}
} }
} }
for (const auto &participant : participants) { for (const auto &participant : participants) {
@ -1406,7 +1373,7 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
} }
void MembersController::loadMoreRows() { void MembersController::loadMoreRows() {
if (const auto real = _peer->groupCall()) { if (const auto real = _call->lookupReal()) {
real->requestParticipants(); real->requestParticipants();
} }
} }
@ -1435,7 +1402,7 @@ void MembersController::rowUpdateRow(not_null<Row*> row) {
void MembersController::rowScheduleRaisedHandStatusRemove( void MembersController::rowScheduleRaisedHandStatusRemove(
not_null<Row*> row) { not_null<Row*> row) {
const auto id = row->peer()->id; const auto id = row->id();
const auto when = crl::now() + kKeepRaisedHandStatusDuration; const auto when = crl::now() + kKeepRaisedHandStatusDuration;
const auto i = _raisedHandStatusRemoveAt.find(id); const auto i = _raisedHandStatusRemoveAt.find(id);
if (i != _raisedHandStatusRemoveAt.end()) { if (i != _raisedHandStatusRemoveAt.end()) {
@ -1634,12 +1601,10 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
} }
if (isMe(participantPeer)) { if (isMe(participantPeer)) {
if (const auto strong = _call.get() if (_call->muted() == MuteState::RaisedHand) {
; strong && strong->muted() == MuteState::RaisedHand) {
const auto removeHand = [=] { const auto removeHand = [=] {
if (const auto strong = _call.get() if (_call->muted() == MuteState::RaisedHand) {
; strong && strong->muted() == MuteState::RaisedHand) { _call->setMutedAndUpdate(MuteState::ForceMuted);
strong->setMutedAndUpdate(MuteState::ForceMuted);
} }
}; };
result->addAction( result->addAction(
@ -1728,14 +1693,12 @@ void MembersController::addMuteActionsToContextMenu(
auto mutesFromVolume = rpl::never<bool>() | rpl::type_erased(); auto mutesFromVolume = rpl::never<bool>() | rpl::type_erased();
const auto call = _call.get(); if (!isMuted || _call->joinAs() == participantPeer) {
if (!isMuted || (call && call->joinAs() == participantPeer)) { auto otherParticipantStateValue
auto otherParticipantStateValue = call = _call->otherParticipantStateValue(
? call->otherParticipantStateValue( ) | rpl::filter([=](const Group::ParticipantState &data) {
) | rpl::filter([=](const Group::ParticipantState &data) { return data.peer == participantPeer;
return data.peer == participantPeer; });
})
: rpl::never<Group::ParticipantState>() | rpl::type_erased();
auto volumeItem = base::make_unique_q<MenuVolumeItem>( auto volumeItem = base::make_unique_q<MenuVolumeItem>(
menu->menu(), menu->menu(),
@ -1814,11 +1777,7 @@ void MembersController::addMuteActionsToContextMenu(
} }
std::unique_ptr<Row> MembersController::createRowForMe() { std::unique_ptr<Row> MembersController::createRowForMe() {
const auto call = _call.get(); auto result = std::make_unique<Row>(this, _call->joinAs());
if (!call) {
return nullptr;
}
auto result = std::make_unique<Row>(this, call->joinAs());
updateRow(result.get(), nullptr); updateRow(result.get(), nullptr);
return result; return result;
} }
@ -1877,12 +1836,8 @@ auto Members::kickParticipantRequests() const
int Members::desiredHeight() const { int Members::desiredHeight() const {
const auto top = _addMember ? _addMember->height() : 0; const auto top = _addMember ? _addMember->height() : 0;
auto count = [&] { auto count = [&] {
if (const auto call = _call.get()) { if (const auto real = _call->lookupReal()) {
if (const auto real = call->peer()->groupCall()) { return real->fullCount();
if (call->id() == real->id()) {
return real->fullCount();
}
}
} }
return 0; return 0;
}(); }();
@ -1911,16 +1866,7 @@ void Members::setupAddMember(not_null<GroupCall*> call) {
if (const auto channel = peer->asBroadcast()) { if (const auto channel = peer->asBroadcast()) {
_canAddMembers = rpl::single( _canAddMembers = rpl::single(
false false
) | rpl::then(peer->session().changes().peerFlagsValue( ) | rpl::then(_call->real(
peer,
Data::PeerUpdate::Flag::GroupCall
) | rpl::map([=] {
return peer->groupCall();
}) | rpl::filter([=](Data::GroupCall *real) {
const auto call = _call.get();
return call && real && (real->id() == call->id());
}) | rpl::take(
1
) | rpl::map([=] { ) | rpl::map([=] {
return Data::PeerFlagValue( return Data::PeerFlagValue(
channel, channel,

View file

@ -75,7 +75,7 @@ private:
void updateControlsGeometry(); void updateControlsGeometry();
const base::weak_ptr<GroupCall> _call; const not_null<GroupCall*> _call;
object_ptr<Ui::ScrollArea> _scroll; object_ptr<Ui::ScrollArea> _scroll;
std::unique_ptr<PeerListController> _listController; std::unique_ptr<PeerListController> _listController;
object_ptr<Ui::SettingsButton> _addMember = { nullptr }; object_ptr<Ui::SettingsButton> _addMember = { nullptr };

View file

@ -514,15 +514,6 @@ base::unique_qptr<Ui::Menu::ItemBase> MakeRecordingAction(
std::move(callback)); std::move(callback));
} }
base::unique_qptr<Ui::Menu::ItemBase> MakeFinishAction(
not_null<Ui::Menu::Menu*> menu,
Fn<void()> callback) {
return MakeAttentionAction(
menu,
tr::lng_group_call_end(tr::now),
std::move(callback));
}
} // namespace } // namespace
void LeaveBox( void LeaveBox(
@ -530,16 +521,25 @@ void LeaveBox(
not_null<GroupCall*> call, not_null<GroupCall*> call,
bool discardChecked, bool discardChecked,
BoxContext context) { BoxContext context) {
box->setTitle(tr::lng_group_call_leave_title()); const auto scheduled = (call->scheduleDate() != 0);
if (!scheduled) {
box->setTitle(tr::lng_group_call_leave_title());
}
const auto inCall = (context == BoxContext::GroupCallPanel); const auto inCall = (context == BoxContext::GroupCallPanel);
box->addRow(object_ptr<Ui::FlatLabel>( box->addRow(
box.get(), object_ptr<Ui::FlatLabel>(
tr::lng_group_call_leave_sure(), box.get(),
(inCall ? st::groupCallBoxLabel : st::boxLabel))); (scheduled
? tr::lng_group_call_close_sure()
: tr::lng_group_call_leave_sure()),
(inCall ? st::groupCallBoxLabel : st::boxLabel)),
scheduled ? st::boxPadding : st::boxRowPadding);
const auto discard = call->peer()->canManageGroupCall() const auto discard = call->peer()->canManageGroupCall()
? box->addRow(object_ptr<Ui::Checkbox>( ? box->addRow(object_ptr<Ui::Checkbox>(
box.get(), box.get(),
tr::lng_group_call_end(), (scheduled
? tr::lng_group_call_also_cancel()
: tr::lng_group_call_also_end()),
discardChecked, discardChecked,
(inCall ? st::groupCallCheckbox : st::defaultBoxCheckbox), (inCall ? st::groupCallCheckbox : st::defaultBoxCheckbox),
(inCall ? st::groupCallCheck : st::defaultCheck)), (inCall ? st::groupCallCheck : st::defaultCheck)),
@ -550,7 +550,10 @@ void LeaveBox(
st::boxRowPadding.bottom())) st::boxRowPadding.bottom()))
: nullptr; : nullptr;
const auto weak = base::make_weak(call.get()); const auto weak = base::make_weak(call.get());
box->addButton(tr::lng_group_call_leave(), [=] { auto label = scheduled
? tr::lng_group_call_close()
: tr::lng_group_call_leave();
box->addButton(std::move(label), [=] {
const auto discardCall = (discard && discard->checked()); const auto discardCall = (discard && discard->checked());
box->closeBox(); box->closeBox();
@ -565,19 +568,20 @@ void LeaveBox(
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
} }
void ConfirmBox( void ConfirmBoxBuilder(
not_null<Ui::GenericBox*> box, not_null<Ui::GenericBox*> box,
const TextWithEntities &text, ConfirmBoxArgs &&args) {
rpl::producer<QString> button, const auto label = box->addRow(
Fn<void()> callback) {
box->addRow(
object_ptr<Ui::FlatLabel>( object_ptr<Ui::FlatLabel>(
box.get(), box.get(),
rpl::single(text), rpl::single(args.text),
st::groupCallBoxLabel), args.st ? *args.st : st::groupCallBoxLabel),
st::boxPadding); st::boxPadding);
if (callback) { if (args.callback) {
box->addButton(std::move(button), callback); box->addButton(std::move(args.button), std::move(args.callback));
}
if (args.filter) {
label->setClickHandlerFilter(std::move(args.filter));
} }
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
} }
@ -603,7 +607,8 @@ void FillMenu(
const auto addEditJoinAs = call->showChooseJoinAs(); const auto addEditJoinAs = call->showChooseJoinAs();
const auto addEditTitle = peer->canManageGroupCall(); const auto addEditTitle = peer->canManageGroupCall();
const auto addEditRecording = peer->canManageGroupCall(); const auto addEditRecording = peer->canManageGroupCall()
&& !real->scheduleDate();
if (addEditJoinAs) { if (addEditJoinAs) {
menu->addAction(MakeJoinAsAction( menu->addAction(MakeJoinAsAction(
menu->menu(), menu->menu(),
@ -660,7 +665,7 @@ void FillMenu(
showBox(Box(SettingsBox, strong)); showBox(Box(SettingsBox, strong));
} }
}); });
menu->addAction(MakeFinishAction(menu->menu(), [=] { const auto finish = [=] {
if (const auto strong = weak.get()) { if (const auto strong = weak.get()) {
showBox(Box( showBox(Box(
LeaveBox, LeaveBox,
@ -668,7 +673,13 @@ void FillMenu(
true, true,
BoxContext::GroupCallPanel)); BoxContext::GroupCallPanel));
} }
})); };
menu->addAction(MakeAttentionAction(
menu->menu(),
(real->scheduleDate()
? tr::lng_group_call_cancel(tr::now)
: tr::lng_group_call_end(tr::now)),
finish));
} }
base::unique_qptr<Ui::Menu::ItemBase> MakeAttentionAction( base::unique_qptr<Ui::Menu::ItemBase> MakeAttentionAction(

View file

@ -9,6 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/object_ptr.h" #include "base/object_ptr.h"
#include "base/unique_qptr.h" #include "base/unique_qptr.h"
#include "ui/layers/generic_box.h"
namespace style {
struct FlatLabel;
} // namespace style
namespace Ui { namespace Ui {
class DropdownMenu; class DropdownMenu;
@ -38,11 +43,19 @@ void LeaveBox(
bool discardChecked, bool discardChecked,
BoxContext context); BoxContext context);
void ConfirmBox( struct ConfirmBoxArgs {
not_null<Ui::GenericBox*> box, TextWithEntities text;
const TextWithEntities &text, rpl::producer<QString> button;
rpl::producer<QString> button, Fn<void()> callback;
Fn<void()> callback); const style::FlatLabel *st = nullptr;
Fn<bool(const ClickHandlerPtr&, Qt::MouseButton)> filter;
};
void ConfirmBoxBuilder(not_null<Ui::GenericBox*> box, ConfirmBoxArgs &&args);
inline auto ConfirmBox(ConfirmBoxArgs &&args) {
return Box(ConfirmBoxBuilder, std::move(args));
}
void FillMenu( void FillMenu(
not_null<Ui::DropdownMenu*> menu, not_null<Ui::DropdownMenu*> menu,

View file

@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/checkbox.h" #include "ui/widgets/checkbox.h"
#include "ui/widgets/dropdown_menu.h" #include "ui/widgets/dropdown_menu.h"
#include "ui/widgets/input_fields.h" #include "ui/widgets/input_fields.h"
#include "ui/chat/group_call_bar.h"
#include "ui/layers/layer_manager.h" #include "ui/layers/layer_manager.h"
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
@ -34,12 +35,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_group_call.h" #include "data/data_group_call.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_peer_values.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "base/event_filter.h" #include "base/event_filter.h"
#include "boxes/peers/edit_participants_box.h" #include "boxes/peers/edit_participants_box.h"
#include "boxes/peers/add_participants_box.h" #include "boxes/peers/add_participants_box.h"
#include "boxes/peer_lists_box.h" #include "boxes/peer_lists_box.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
#include "base/unixtime.h"
#include "base/timer_rpl.h"
#include "app.h" #include "app.h"
#include "apiwrap.h" // api().kickParticipant. #include "apiwrap.h" // api().kickParticipant.
#include "styles/style_calls.h" #include "styles/style_calls.h"
@ -55,6 +59,7 @@ namespace {
constexpr auto kSpacePushToTalkDelay = crl::time(250); constexpr auto kSpacePushToTalkDelay = crl::time(250);
constexpr auto kRecordingAnimationDuration = crl::time(1200); constexpr auto kRecordingAnimationDuration = crl::time(1200);
constexpr auto kRecordingOpacity = 0.6; constexpr auto kRecordingOpacity = 0.6;
constexpr auto kStartNoConfirmation = TimeId(10);
class InviteController final : public ParticipantsBoxController { class InviteController final : public ParticipantsBoxController {
public: public:
@ -115,6 +120,123 @@ private:
}; };
[[nodiscard]] rpl::producer<QString> StartsWhenText(
rpl::producer<TimeId> date) {
return std::move(
date
) | rpl::map([](TimeId date) -> rpl::producer<QString> {
const auto parsedDate = base::unixtime::parse(date);
const auto dateDay = QDateTime(parsedDate.date(), QTime(0, 0));
const auto previousDay = QDateTime(
parsedDate.date().addDays(-1),
QTime(0, 0));
const auto now = QDateTime::currentDateTime();
const auto kDay = int64(24 * 60 * 60);
const auto tillTomorrow = int64(now.secsTo(previousDay));
const auto tillToday = tillTomorrow + kDay;
const auto tillAfter = tillToday + kDay;
const auto time = parsedDate.time().toString(
QLocale::system().timeFormat(QLocale::ShortFormat));
auto exact = tr::lng_group_call_starts_short_date(
lt_date,
rpl::single(langDayOfMonthFull(dateDay.date())),
lt_time,
rpl::single(time)
) | rpl::type_erased();
auto tomorrow = tr::lng_group_call_starts_short_tomorrow(
lt_time,
rpl::single(time));
auto today = tr::lng_group_call_starts_short_today(
lt_time,
rpl::single(time));
auto todayAndAfter = rpl::single(
std::move(today)
) | rpl::then(base::timer_once(
std::min(tillAfter, kDay) * crl::time(1000)
) | rpl::map([=] {
return rpl::duplicate(exact);
})) | rpl::flatten_latest() | rpl::type_erased();
auto tomorrowAndAfter = rpl::single(
std::move(tomorrow)
) | rpl::then(base::timer_once(
std::min(tillToday, kDay) * crl::time(1000)
) | rpl::map([=] {
return rpl::duplicate(todayAndAfter);
})) | rpl::flatten_latest() | rpl::type_erased();
auto full = rpl::single(
rpl::duplicate(exact)
) | rpl::then(base::timer_once(
tillTomorrow * crl::time(1000)
) | rpl::map([=] {
return rpl::duplicate(tomorrowAndAfter);
})) | rpl::flatten_latest() | rpl::type_erased();
if (tillTomorrow > 0) {
return full;
} else if (tillToday > 0) {
return tomorrowAndAfter;
} else if (tillAfter > 0) {
return todayAndAfter;
} else {
return exact;
}
}) | rpl::flatten_latest();
}
[[nodiscard]] object_ptr<Ui::RpWidget> CreateGradientLabel(
QWidget *parent,
rpl::producer<QString> text) {
struct State {
QBrush brush;
QPainterPath path;
};
auto result = object_ptr<Ui::RpWidget>(parent);
const auto raw = result.data();
const auto state = raw->lifetime().make_state<State>();
std::move(
text
) | rpl::start_with_next([=](const QString &text) {
state->path = QPainterPath();
const auto &font = st::groupCallCountdownFont;
state->path.addText(0, font->ascent, font->f, text);
const auto width = font->width(text);
raw->resize(width, font->height);
auto gradient = QLinearGradient(QPoint(width, 0), QPoint());
gradient.setStops(QGradientStops{
{ 0.0, st::groupCallForceMutedBar1->c },
{ .7, st::groupCallForceMutedBar2->c },
{ 1.0, st::groupCallForceMutedBar3->c }
});
state->brush = QBrush(std::move(gradient));
raw->update();
}, raw->lifetime());
raw->paintRequest(
) | rpl::start_with_next([=] {
auto p = QPainter(raw);
auto hq = PainterHighQualityEnabler(p);
const auto skip = st::groupCallWidth / 20;
const auto available = parent->width() - 2 * skip;
const auto full = raw->width();
if (available > 0 && full > available) {
const auto scale = available / float64(full);
const auto shift = raw->rect().center();
p.translate(shift);
p.scale(scale, scale);
p.translate(-shift);
}
p.setPen(Qt::NoPen);
p.setBrush(state->brush);
p.drawPath(state->path);
}, raw->lifetime());
return result;
}
[[nodiscard]] object_ptr<Ui::RpWidget> CreateSectionSubtitle( [[nodiscard]] object_ptr<Ui::RpWidget> CreateSectionSubtitle(
QWidget *parent, QWidget *parent,
rpl::producer<QString> text) { rpl::producer<QString> text) {
@ -181,7 +303,7 @@ void InviteController::itemDeselectedHook(not_null<PeerData*> peer) {
} }
bool InviteController::hasRowFor(not_null<PeerData*> peer) const { bool InviteController::hasRowFor(not_null<PeerData*> peer) const {
return (delegate()->peerListFindRow(peer->id) != nullptr); return (delegate()->peerListFindRow(peer->id.value) != nullptr);
} }
bool InviteController::isAlreadyIn(not_null<UserData*> user) const { bool InviteController::isAlreadyIn(not_null<UserData*> user) const {
@ -235,7 +357,7 @@ void InviteContactsController::prepareViewHook() {
std::move( std::move(
_discoveredInGroup _discoveredInGroup
) | rpl::start_with_next([=](not_null<UserData*> user) { ) | rpl::start_with_next([=](not_null<UserData*> user) {
if (auto row = delegate()->peerListFindRow(user->id)) { if (auto row = delegate()->peerListFindRow(user->id.value)) {
delegate()->peerListRemoveRow(row); delegate()->peerListRemoveRow(row);
} }
}, _lifetime); }, _lifetime);
@ -260,25 +382,30 @@ Panel::Panel(not_null<GroupCall*> call)
_window->body(), _window->body(),
st::groupCallTitle)) st::groupCallTitle))
#endif // !Q_OS_MAC #endif // !Q_OS_MAC
, _members(widget(), call)
, _settings(widget(), st::groupCallSettings)
, _mute(std::make_unique<Ui::CallMuteButton>( , _mute(std::make_unique<Ui::CallMuteButton>(
widget(), widget(),
Core::App().appDeactivatedValue(), Core::App().appDeactivatedValue(),
Ui::CallMuteButtonState{ Ui::CallMuteButtonState{
.text = tr::lng_group_call_connecting(tr::now), .text = (_call->scheduleDate()
.type = Ui::CallMuteButtonType::Connecting, ? tr::lng_group_call_start_now(tr::now)
: tr::lng_group_call_connecting(tr::now)),
.type = (!_call->scheduleDate()
? Ui::CallMuteButtonType::Connecting
: _peer->canManageGroupCall()
? Ui::CallMuteButtonType::ScheduledCanStart
: _call->scheduleStartSubscribed()
? Ui::CallMuteButtonType::ScheduledNotify
: Ui::CallMuteButtonType::ScheduledSilent),
})) }))
, _hangup(widget(), st::groupCallHangup) { , _hangup(widget(), st::groupCallHangup) {
_layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox); _layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox);
_settings->setColorOverrides(_mute->colorOverrides());
_layerBg->setHideByBackgroundClick(true); _layerBg->setHideByBackgroundClick(true);
SubscribeToMigration( SubscribeToMigration(
_peer, _peer,
_window->lifetime(), _window->lifetime(),
[=](not_null<ChannelData*> channel) { migrate(channel); }); [=](not_null<ChannelData*> channel) { migrate(channel); });
setupRealCallViewers(call); setupRealCallViewers();
initWindow(); initWindow();
initWidget(); initWidget();
@ -287,30 +414,7 @@ Panel::Panel(not_null<GroupCall*> call)
showAndActivate(); showAndActivate();
setupJoinAsChangedToasts(); setupJoinAsChangedToasts();
setupTitleChangedToasts(); setupTitleChangedToasts();
setupAllowedToSpeakToasts();
call->allowedToSpeakNotifications(
) | rpl::start_with_next([=] {
if (isActive()) {
Ui::ShowMultilineToast({
.parentOverride = widget(),
.text = { tr::lng_group_call_can_speak_here(tr::now) },
});
} else {
const auto real = _peer->groupCall();
const auto name = (real
&& (real->id() == call->id())
&& !real->title().isEmpty())
? real->title()
: _peer->name;
Ui::ShowMultilineToast({
.text = tr::lng_group_call_can_speak(
tr::now,
lt_chat,
Ui::Text::Bold(name),
Ui::Text::WithEntities),
});
}
}, widget()->lifetime());
} }
Panel::~Panel() { Panel::~Panel() {
@ -319,17 +423,8 @@ Panel::~Panel() {
} }
} }
void Panel::setupRealCallViewers(not_null<GroupCall*> call) { void Panel::setupRealCallViewers() {
const auto peer = call->peer(); _call->real(
peer->session().changes().peerFlagsValue(
peer,
Data::PeerUpdate::Flag::GroupCall
) | rpl::map([=] {
return peer->groupCall();
}) | rpl::filter([=](Data::GroupCall *real) {
return _call && real && (real->id() == _call->id());
}) | rpl::take(
1
) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) { ) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) {
subscribeToChanges(real); subscribeToChanges(real);
}, _window->lifetime()); }, _window->lifetime());
@ -394,11 +489,9 @@ void Panel::initWindow() {
} else if (e->type() == QEvent::KeyPress } else if (e->type() == QEvent::KeyPress
|| e->type() == QEvent::KeyRelease) { || e->type() == QEvent::KeyRelease) {
if (static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Space) { if (static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Space) {
if (_call) { _call->pushToTalk(
_call->pushToTalk( e->type() == QEvent::KeyPress,
e->type() == QEvent::KeyPress, kSpacePushToTalkDelay);
kSpacePushToTalkDelay);
}
} }
} }
return base::EventFilterResult::Continue; return base::EventFilterResult::Continue;
@ -440,9 +533,7 @@ void Panel::initWidget() {
} }
void Panel::endCall() { void Panel::endCall() {
if (!_call) { if (!_call->peer()->canManageGroupCall()) {
return;
} else if (!_call->peer()->canManageGroupCall()) {
_call->hangup(); _call->hangup();
return; return;
} }
@ -453,11 +544,45 @@ void Panel::endCall() {
BoxContext::GroupCallPanel)); BoxContext::GroupCallPanel));
} }
void Panel::startScheduledNow() {
const auto date = _call->scheduleDate();
const auto now = base::unixtime::now();
if (!date) {
return;
} else if (now + kStartNoConfirmation >= date) {
_call->startScheduledNow();
} else {
const auto box = std::make_shared<QPointer<Ui::GenericBox>>();
const auto done = [=] {
if (*box) {
(*box)->closeBox();
}
_call->startScheduledNow();
};
auto owned = ConfirmBox({
.text = { tr::lng_group_call_start_now_sure(tr::now) },
.button = tr::lng_group_call_start_now(),
.callback = done,
});
*box = owned.data();
_layerBg->showBox(std::move(owned));
}
}
void Panel::initControls() { void Panel::initControls() {
_mute->clicks( _mute->clicks(
) | rpl::filter([=](Qt::MouseButton button) { ) | rpl::filter([=](Qt::MouseButton button) {
return (button == Qt::LeftButton) && (_call != nullptr); return (button == Qt::LeftButton);
}) | rpl::start_with_next([=] { }) | rpl::start_with_next([=] {
if (_call->scheduleDate()) {
if (_peer->canManageGroupCall()) {
startScheduledNow();
} else if (const auto real = _call->lookupReal()) {
_call->toggleScheduleStartSubscribed(
!real->scheduleStartSubscribed());
}
return;
}
const auto oldState = _call->muted(); const auto oldState = _call->muted();
const auto newState = (oldState == MuteState::ForceMuted) const auto newState = (oldState == MuteState::ForceMuted)
? MuteState::RaisedHand ? MuteState::RaisedHand
@ -469,34 +594,47 @@ void Panel::initControls() {
_call->setMutedAndUpdate(newState); _call->setMutedAndUpdate(newState);
}, _mute->lifetime()); }, _mute->lifetime());
initShareAction();
refreshLeftButton();
_hangup->setClickedCallback([=] { endCall(); }); _hangup->setClickedCallback([=] { endCall(); });
_settings->setClickedCallback([=] {
if (_call) {
_layerBg->showBox(Box(SettingsBox, _call));
}
});
_settings->setText(tr::lng_group_call_settings()); const auto scheduleDate = _call->scheduleDate();
_hangup->setText(tr::lng_group_call_leave()); _hangup->setText(scheduleDate
? tr::lng_group_call_close()
: tr::lng_group_call_leave());
if (scheduleDate) {
auto changes = _call->real(
) | rpl::map([=](not_null<Data::GroupCall*> real) {
return real->scheduleDateValue();
}) | rpl::flatten_latest();
_members->desiredHeightValue( setupScheduledLabels(rpl::single(
) | rpl::start_with_next([=] { scheduleDate
updateControlsGeometry(); ) | rpl::then(rpl::duplicate(changes)));
}, _members->lifetime());
initWithCall(_call); auto started = std::move(changes) | rpl::filter([](TimeId date) {
} return (date == 0);
}) | rpl::take(1);
void Panel::initWithCall(GroupCall *call) { rpl::merge(
_callLifetime.destroy(); rpl::duplicate(started) | rpl::to_empty,
_call = call; _peer->session().changes().peerFlagsValue(
if (!_call) { _peer,
return; Data::PeerUpdate::Flag::Username
) | rpl::skip(1) | rpl::to_empty
) | rpl::start_with_next([=] {
refreshLeftButton();
updateControlsGeometry();
}, _callLifetime);
std::move(started) | rpl::start_with_next([=] {
_hangup->setText(tr::lng_group_call_leave());
setupMembers();
}, _callLifetime);
} }
_peer = _call->peer(); _call->stateValue(
call->stateValue(
) | rpl::filter([](State state) { ) | rpl::filter([](State state) {
return (state == State::HangingUp) return (state == State::HangingUp)
|| (state == State::Ended) || (state == State::Ended)
@ -506,13 +644,214 @@ void Panel::initWithCall(GroupCall *call) {
closeBeforeDestroy(); closeBeforeDestroy();
}, _callLifetime); }, _callLifetime);
call->levelUpdates( _call->levelUpdates(
) | rpl::filter([=](const LevelUpdate &update) { ) | rpl::filter([=](const LevelUpdate &update) {
return update.me; return update.me;
}) | rpl::start_with_next([=](const LevelUpdate &update) { }) | rpl::start_with_next([=](const LevelUpdate &update) {
_mute->setLevel(update.value); _mute->setLevel(update.value);
}, _callLifetime); }, _callLifetime);
_call->real(
) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) {
setupRealMuteButtonState(real);
}, _callLifetime);
}
void Panel::refreshLeftButton() {
const auto share = _call->scheduleDate()
&& _peer->isBroadcast()
&& _peer->asChannel()->hasUsername();
if ((share && _share) || (!share && _settings)) {
return;
}
if (share) {
_settings.destroy();
_share.create(widget(), st::groupCallShare);
_share->setClickedCallback(_shareLinkCallback);
_share->setText(tr::lng_group_call_share_button());
} else {
_share.destroy();
_settings.create(widget(), st::groupCallSettings);
_settings->setClickedCallback([=] {
_layerBg->showBox(Box(SettingsBox, _call));
});
_settings->setText(tr::lng_group_call_settings());
}
const auto raw = _share ? _share.data() : _settings.data();
raw->show();
raw->setColorOverrides(_mute->colorOverrides());
}
void Panel::initShareAction() {
const auto showBox = [=](object_ptr<Ui::BoxContent> next) {
_layerBg->showBox(std::move(next));
};
const auto showToast = [=](QString text) {
Ui::ShowMultilineToast({
.parentOverride = widget(),
.text = { text },
});
};
auto [shareLinkCallback, shareLinkLifetime] = ShareInviteLinkAction(
_peer,
showBox,
showToast);
_shareLinkCallback = [=, callback = std::move(shareLinkCallback)] {
if (_call->lookupReal()) {
callback();
}
};
widget()->lifetime().add(std::move(shareLinkLifetime));
}
void Panel::setupRealMuteButtonState(not_null<Data::GroupCall*> real) {
using namespace rpl::mappers;
rpl::combine(
_call->mutedValue() | MapPushToTalkToActive(),
_call->instanceStateValue(),
real->scheduleDateValue(),
real->scheduleStartSubscribedValue(),
Data::CanManageGroupCallValue(_peer)
) | rpl::distinct_until_changed(
) | rpl::filter(
_2 != GroupCall::InstanceState::TransitionToRtc
) | rpl::start_with_next([=](
MuteState mute,
GroupCall::InstanceState state,
TimeId scheduleDate,
bool scheduleStartSubscribed,
bool canManage) {
using Type = Ui::CallMuteButtonType;
_mute->setState(Ui::CallMuteButtonState{
.text = (scheduleDate
? (canManage
? tr::lng_group_call_start_now(tr::now)
: scheduleStartSubscribed
? tr::lng_group_call_cancel_reminder(tr::now)
: tr::lng_group_call_set_reminder(tr::now))
: state == GroupCall::InstanceState::Disconnected
? tr::lng_group_call_connecting(tr::now)
: mute == MuteState::ForceMuted
? tr::lng_group_call_force_muted(tr::now)
: mute == MuteState::RaisedHand
? tr::lng_group_call_raised_hand(tr::now)
: mute == MuteState::Muted
? tr::lng_group_call_unmute(tr::now)
: tr::lng_group_call_you_are_live(tr::now)),
.subtext = (scheduleDate
? QString()
: state == GroupCall::InstanceState::Disconnected
? QString()
: mute == MuteState::ForceMuted
? tr::lng_group_call_raise_hand_tip(tr::now)
: mute == MuteState::RaisedHand
? tr::lng_group_call_raised_hand_sub(tr::now)
: mute == MuteState::Muted
? tr::lng_group_call_unmute_sub(tr::now)
: QString()),
.type = (scheduleDate
? (canManage
? Type::ScheduledCanStart
: scheduleStartSubscribed
? Type::ScheduledNotify
: Type::ScheduledSilent)
: state == GroupCall::InstanceState::Disconnected
? Type::Connecting
: mute == MuteState::ForceMuted
? Type::ForceMuted
: mute == MuteState::RaisedHand
? Type::RaisedHand
: mute == MuteState::Muted
? Type::Muted
: Type::Active),
});
}, _callLifetime);
}
void Panel::setupScheduledLabels(rpl::producer<TimeId> date) {
using namespace rpl::mappers;
date = std::move(date) | rpl::take_while(_1 != 0);
_startsWhen.create(
widget(),
StartsWhenText(rpl::duplicate(date)),
st::groupCallStartsWhen);
auto countdownCreated = std::move(
date
) | rpl::map([=](TimeId date) {
_countdownData = std::make_shared<Ui::GroupCallScheduledLeft>(date);
return rpl::empty_value();
}) | rpl::start_spawning(widget()->lifetime());
_countdown = CreateGradientLabel(widget(), rpl::duplicate(
countdownCreated
) | rpl::map([=] {
return _countdownData->text(
Ui::GroupCallScheduledLeft::Negative::Ignore);
}) | rpl::flatten_latest());
_startsIn.create(
widget(),
rpl::conditional(
std::move(
countdownCreated
) | rpl::map(
[=] { return _countdownData->late(); }
) | rpl::flatten_latest(),
tr::lng_group_call_late_by(),
tr::lng_group_call_starts_in()),
st::groupCallStartsIn);
const auto top = [=] {
const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip;
const auto membersTop = st::groupCallMembersTop;
const auto height = st::groupCallScheduledBodyHeight;
return (membersTop + (muteTop - membersTop - height) / 2);
};
rpl::combine(
widget()->sizeValue(),
_startsIn->widthValue()
) | rpl::start_with_next([=](QSize size, int width) {
_startsIn->move(
(size.width() - width) / 2,
top() + st::groupCallStartsInTop);
}, _startsIn->lifetime());
rpl::combine(
widget()->sizeValue(),
_startsWhen->widthValue()
) | rpl::start_with_next([=](QSize size, int width) {
_startsWhen->move(
(size.width() - width) / 2,
top() + st::groupCallStartsWhenTop);
}, _startsWhen->lifetime());
rpl::combine(
widget()->sizeValue(),
_countdown->widthValue()
) | rpl::start_with_next([=](QSize size, int width) {
_countdown->move(
(size.width() - width) / 2,
top() + st::groupCallCountdownTop);
}, _startsWhen->lifetime());
}
void Panel::setupMembers() {
if (_members) {
return;
}
_startsIn.destroy();
_countdown.destroy();
_startsWhen.destroy();
_members.create(widget(), _call);
_members->show();
_members->desiredHeightValue(
) | rpl::start_with_next([=] {
updateMembersGeometry();
}, _members->lifetime());
_members->toggleMuteRequests( _members->toggleMuteRequests(
) | rpl::start_with_next([=](MuteRequest request) { ) | rpl::start_with_next([=](MuteRequest request) {
if (_call) { if (_call) {
@ -532,73 +871,14 @@ void Panel::initWithCall(GroupCall *call) {
kickParticipant(participantPeer); kickParticipant(participantPeer);
}, _callLifetime); }, _callLifetime);
const auto showBox = [=](object_ptr<Ui::BoxContent> next) {
_layerBg->showBox(std::move(next));
};
const auto showToast = [=](QString text) {
Ui::ShowMultilineToast({
.parentOverride = widget(),
.text = { text },
});
};
auto [shareLinkCallback, shareLinkLifetime] = ShareInviteLinkAction(
_peer,
showBox,
showToast);
auto shareLink = std::move(shareLinkCallback);
_members->lifetime().add(std::move(shareLinkLifetime));
_members->addMembersRequests( _members->addMembersRequests(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
if (_call) { if (_peer->isBroadcast() && _peer->asChannel()->hasUsername()) {
if (_peer->isBroadcast() && _peer->asChannel()->hasUsername()) { _shareLinkCallback();
shareLink(); } else {
} else { addMembers();
addMembers();
}
} }
}, _callLifetime); }, _callLifetime);
using namespace rpl::mappers;
rpl::combine(
_call->mutedValue() | MapPushToTalkToActive(),
_call->instanceStateValue()
) | rpl::distinct_until_changed(
) | rpl::filter(
_2 != GroupCall::InstanceState::TransitionToRtc
) | rpl::start_with_next([=](
MuteState mute,
GroupCall::InstanceState state) {
_mute->setState(Ui::CallMuteButtonState{
.text = (state == GroupCall::InstanceState::Disconnected
? tr::lng_group_call_connecting(tr::now)
: mute == MuteState::ForceMuted
? tr::lng_group_call_force_muted(tr::now)
: mute == MuteState::RaisedHand
? tr::lng_group_call_raised_hand(tr::now)
: mute == MuteState::Muted
? tr::lng_group_call_unmute(tr::now)
: tr::lng_group_call_you_are_live(tr::now)),
.subtext = (state == GroupCall::InstanceState::Disconnected
? QString()
: mute == MuteState::ForceMuted
? tr::lng_group_call_raise_hand_tip(tr::now)
: mute == MuteState::RaisedHand
? tr::lng_group_call_raised_hand_sub(tr::now)
: mute == MuteState::Muted
? tr::lng_group_call_unmute_sub(tr::now)
: QString()),
.type = (state == GroupCall::InstanceState::Disconnected
? Ui::CallMuteButtonType::Connecting
: mute == MuteState::ForceMuted
? Ui::CallMuteButtonType::ForceMuted
: mute == MuteState::RaisedHand
? Ui::CallMuteButtonType::RaisedHand
: mute == MuteState::Muted
? Ui::CallMuteButtonType::Muted
: Ui::CallMuteButtonType::Active),
});
}, _callLifetime);
} }
void Panel::setupJoinAsChangedToasts() { void Panel::setupJoinAsChangedToasts() {
@ -624,7 +904,7 @@ void Panel::setupJoinAsChangedToasts() {
void Panel::setupTitleChangedToasts() { void Panel::setupTitleChangedToasts() {
_call->titleChanged( _call->titleChanged(
) | rpl::filter([=] { ) | rpl::filter([=] {
return _peer->groupCall() && _peer->groupCall()->id() == _call->id(); return (_call->lookupReal() != nullptr);
}) | rpl::map([=] { }) | rpl::map([=] {
return _peer->groupCall()->title().isEmpty() return _peer->groupCall()->title().isEmpty()
? _peer->name ? _peer->name
@ -641,9 +921,31 @@ void Panel::setupTitleChangedToasts() {
}, widget()->lifetime()); }, widget()->lifetime());
} }
void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) { void Panel::setupAllowedToSpeakToasts() {
_titleText = real->titleValue(); _call->allowedToSpeakNotifications(
) | rpl::start_with_next([=] {
if (isActive()) {
Ui::ShowMultilineToast({
.parentOverride = widget(),
.text = { tr::lng_group_call_can_speak_here(tr::now) },
});
} else {
const auto real = _call->lookupReal();
const auto name = (real && !real->title().isEmpty())
? real->title()
: _peer->name;
Ui::ShowMultilineToast({
.text = tr::lng_group_call_can_speak(
tr::now,
lt_chat,
Ui::Text::Bold(name),
Ui::Text::WithEntities),
});
}
}, widget()->lifetime());
}
void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
const auto validateRecordingMark = [=](bool recording) { const auto validateRecordingMark = [=](bool recording) {
if (!recording && _recordingMark) { if (!recording && _recordingMark) {
_recordingMark.destroy(); _recordingMark.destroy();
@ -703,7 +1005,7 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
.parentOverride = widget(), .parentOverride = widget(),
.text = (recorded .text = (recorded
? tr::lng_group_call_recording_started ? tr::lng_group_call_recording_started
: (_call && _call->recordingStoppedByMe()) : _call->recordingStoppedByMe()
? tr::lng_group_call_recording_saved ? tr::lng_group_call_recording_saved
: tr::lng_group_call_recording_stopped)( : tr::lng_group_call_recording_stopped)(
tr::now, tr::now,
@ -752,9 +1054,7 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
void Panel::chooseJoinAs() { void Panel::chooseJoinAs() {
const auto context = ChooseJoinAsProcess::Context::Switch; const auto context = ChooseJoinAsProcess::Context::Switch;
const auto callback = [=](JoinInfo info) { const auto callback = [=](JoinInfo info) {
if (_call) { _call->rejoinAs(info);
_call->rejoinAs(info);
}
}; };
const auto showBox = [=](object_ptr<Ui::BoxContent> next) { const auto showBox = [=](object_ptr<Ui::BoxContent> next) {
_layerBg->showBox(std::move(next)); _layerBg->showBox(std::move(next));
@ -775,7 +1075,7 @@ void Panel::chooseJoinAs() {
} }
void Panel::showMainMenu() { void Panel::showMainMenu() {
if (_menu || !_call) { if (_menu) {
return; return;
} }
_menu.create(widget(), st::groupCallDropdownMenu); _menu.create(widget(), st::groupCallDropdownMenu);
@ -822,8 +1122,8 @@ void Panel::showMainMenu() {
} }
void Panel::addMembers() { void Panel::addMembers() {
const auto real = _peer->groupCall(); const auto real = _call->lookupReal();
if (!_call || !real || real->id() != _call->id()) { if (!real) {
return; return;
} }
auto alreadyIn = _peer->owner().invitedToCallUsers(real->id()); auto alreadyIn = _peer->owner().invitedToCallUsers(real->id());
@ -849,7 +1149,7 @@ void Panel::addMembers() {
&st::groupCallInviteMembersList, &st::groupCallInviteMembersList,
&st::groupCallMultiSelect); &st::groupCallMultiSelect);
const auto weak = base::make_weak(_call); const auto weak = base::make_weak(_call.get());
const auto invite = [=](const std::vector<not_null<UserData*>> &users) { const auto invite = [=](const std::vector<not_null<UserData*>> &users) {
const auto call = weak.get(); const auto call = weak.get();
if (!call) { if (!call) {
@ -916,11 +1216,14 @@ void Panel::addMembers() {
} }
finish(); finish();
}; };
auto box = Box( const auto done = [=] {
ConfirmBox, inviteWithAdd(users, nonMembers, finishWithConfirm);
TextWithEntities{ text }, };
tr::lng_participant_invite(), auto box = ConfirmBox({
[=] { inviteWithAdd(users, nonMembers, finishWithConfirm); }); .text = { text },
.button = tr::lng_participant_invite(),
.callback = done,
});
*shared = box.data(); *shared = box.data();
_layerBg->showBox(std::move(box)); _layerBg->showBox(std::move(box));
}; };
@ -1032,7 +1335,7 @@ void Panel::showControls() {
void Panel::closeBeforeDestroy() { void Panel::closeBeforeDestroy() {
_window->close(); _window->close();
initWithCall(nullptr); _callLifetime.destroy();
} }
void Panel::initGeometry() { void Panel::initGeometry() {
@ -1064,38 +1367,26 @@ QRect Panel::computeTitleRect() const {
} }
void Panel::updateControlsGeometry() { void Panel::updateControlsGeometry() {
if (widget()->size().isEmpty()) { if (widget()->size().isEmpty() || (!_settings && !_share)) {
return; return;
} }
const auto desiredHeight = _members->desiredHeight();
const auto membersWidthAvailable = widget()->width()
- st::groupCallMembersMargin.left()
- st::groupCallMembersMargin.right();
const auto membersWidthMin = st::groupCallWidth
- st::groupCallMembersMargin.left()
- st::groupCallMembersMargin.right();
const auto membersWidth = std::clamp(
membersWidthAvailable,
membersWidthMin,
st::groupCallMembersWidthMax);
const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip; const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip;
const auto buttonsTop = widget()->height() - st::groupCallButtonBottomSkip; const auto buttonsTop = widget()->height() - st::groupCallButtonBottomSkip;
const auto membersTop = st::groupCallMembersTop;
const auto availableHeight = muteTop
- membersTop
- st::groupCallMembersMargin.bottom();
_members->setGeometry(
(widget()->width() - membersWidth) / 2,
membersTop,
membersWidth,
std::min(desiredHeight, availableHeight));
const auto muteSize = _mute->innerSize().width(); const auto muteSize = _mute->innerSize().width();
const auto fullWidth = muteSize const auto fullWidth = muteSize
+ 2 * _settings->width() + 2 * (_settings ? _settings : _share)->width()
+ 2 * st::groupCallButtonSkip; + 2 * st::groupCallButtonSkip;
_mute->moveInner({ (widget()->width() - muteSize) / 2, muteTop }); _mute->moveInner({ (widget()->width() - muteSize) / 2, muteTop });
_settings->moveToLeft((widget()->width() - fullWidth) / 2, buttonsTop); const auto leftButtonLeft = (widget()->width() - fullWidth) / 2;
_hangup->moveToRight((widget()->width() - fullWidth) / 2, buttonsTop); if (_settings) {
_settings->moveToLeft(leftButtonLeft, buttonsTop);
}
if (_share) {
_share->moveToLeft(leftButtonLeft, buttonsTop);
}
_hangup->moveToRight(leftButtonLeft, buttonsTop);
updateMembersGeometry();
refreshTitle(); refreshTitle();
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
@ -1121,11 +1412,43 @@ void Panel::updateControlsGeometry() {
} }
} }
void Panel::updateMembersGeometry() {
if (!_members) {
return;
}
const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip;
const auto membersTop = st::groupCallMembersTop;
const auto availableHeight = muteTop
- membersTop
- st::groupCallMembersMargin.bottom();
const auto desiredHeight = _members->desiredHeight();
const auto membersWidthAvailable = widget()->width()
- st::groupCallMembersMargin.left()
- st::groupCallMembersMargin.right();
const auto membersWidthMin = st::groupCallWidth
- st::groupCallMembersMargin.left()
- st::groupCallMembersMargin.right();
const auto membersWidth = std::clamp(
membersWidthAvailable,
membersWidthMin,
st::groupCallMembersWidthMax);
_members->setGeometry(
(widget()->width() - membersWidth) / 2,
membersTop,
membersWidth,
std::min(desiredHeight, availableHeight));
}
void Panel::refreshTitle() { void Panel::refreshTitle() {
if (!_title) { if (!_title) {
auto text = rpl::combine( auto text = rpl::combine(
Info::Profile::NameValue(_peer), Info::Profile::NameValue(_peer),
_titleText.value() rpl::single(
QString()
) | rpl::then(_call->real(
) | rpl::map([=](not_null<Data::GroupCall*> real) {
return real->titleValue();
}) | rpl::flatten_latest())
) | rpl::map([=]( ) | rpl::map([=](
const TextWithEntities &name, const TextWithEntities &name,
const QString &title) { const QString &title) {
@ -1144,11 +1467,25 @@ void Panel::refreshTitle() {
if (!_subtitle) { if (!_subtitle) {
_subtitle.create( _subtitle.create(
widget(), widget(),
tr::lng_group_call_members( rpl::single(
lt_count_decimal, _call->scheduleDate()
_members->fullCountValue() | rpl::map([](int value) { ) | rpl::then(
return (value > 0) ? float64(value) : 1.; _call->real(
})), ) | rpl::map([=](not_null<Data::GroupCall*> real) {
return real->scheduleDateValue();
}) | rpl::flatten_latest()
) | rpl::map([=](TimeId scheduleDate) {
if (scheduleDate) {
return tr::lng_group_call_scheduled_status();
} else if (!_members) {
setupMembers();
}
return tr::lng_group_call_members(
lt_count_decimal,
_members->fullCountValue() | rpl::map([](int value) {
return (value > 0) ? float64(value) : 1.;
}));
}) | rpl::flatten_latest(),
st::groupCallSubtitleLabel); st::groupCallSubtitleLabel);
_subtitle->show(); _subtitle->show();
_subtitle->setAttribute(Qt::WA_TransparentForMouseEvents); _subtitle->setAttribute(Qt::WA_TransparentForMouseEvents);

View file

@ -38,6 +38,7 @@ class Window;
class ScrollArea; class ScrollArea;
class GenericBox; class GenericBox;
class LayerManager; class LayerManager;
class GroupCallScheduledLeft;
namespace Platform { namespace Platform {
class TitleControls; class TitleControls;
} // namespace Platform } // namespace Platform
@ -73,16 +74,23 @@ private:
void initWindow(); void initWindow();
void initWidget(); void initWidget();
void initControls(); void initControls();
void initWithCall(GroupCall *call); void initShareAction();
void initLayout(); void initLayout();
void initGeometry(); void initGeometry();
void setupScheduledLabels(rpl::producer<TimeId> date);
void setupMembers();
void setupJoinAsChangedToasts(); void setupJoinAsChangedToasts();
void setupTitleChangedToasts(); void setupTitleChangedToasts();
void setupAllowedToSpeakToasts();
void setupRealMuteButtonState(not_null<Data::GroupCall*> real);
bool handleClose(); bool handleClose();
void startScheduledNow();
void updateControlsGeometry(); void updateControlsGeometry();
void updateMembersGeometry();
void showControls(); void showControls();
void refreshLeftButton();
void endCall(); void endCall();
@ -94,13 +102,13 @@ private:
[[nodiscard]] QRect computeTitleRect() const; [[nodiscard]] QRect computeTitleRect() const;
void refreshTitle(); void refreshTitle();
void refreshTitleGeometry(); void refreshTitleGeometry();
void setupRealCallViewers(not_null<GroupCall*> call); void setupRealCallViewers();
void subscribeToChanges(not_null<Data::GroupCall*> real); void subscribeToChanges(not_null<Data::GroupCall*> real);
void migrate(not_null<ChannelData*> channel); void migrate(not_null<ChannelData*> channel);
void subscribeToPeerChanges(); void subscribeToPeerChanges();
GroupCall *_call = nullptr; const not_null<GroupCall*> _call;
not_null<PeerData*> _peer; not_null<PeerData*> _peer;
const std::unique_ptr<Ui::Window> _window; const std::unique_ptr<Ui::Window> _window;
@ -118,13 +126,18 @@ private:
object_ptr<Ui::IconButton> _menuToggle = { nullptr }; object_ptr<Ui::IconButton> _menuToggle = { nullptr };
object_ptr<Ui::DropdownMenu> _menu = { nullptr }; object_ptr<Ui::DropdownMenu> _menu = { nullptr };
object_ptr<Ui::AbstractButton> _joinAsToggle = { nullptr }; object_ptr<Ui::AbstractButton> _joinAsToggle = { nullptr };
object_ptr<Members> _members; object_ptr<Members> _members = { nullptr };
rpl::variable<QString> _titleText; object_ptr<Ui::FlatLabel> _startsIn = { nullptr };
object_ptr<Ui::RpWidget> _countdown = { nullptr };
std::shared_ptr<Ui::GroupCallScheduledLeft> _countdownData;
object_ptr<Ui::FlatLabel> _startsWhen = { nullptr };
ChooseJoinAsProcess _joinAsProcess; ChooseJoinAsProcess _joinAsProcess;
object_ptr<Ui::CallButton> _settings; object_ptr<Ui::CallButton> _settings = { nullptr };
object_ptr<Ui::CallButton> _share = { nullptr };
std::unique_ptr<Ui::CallMuteButton> _mute; std::unique_ptr<Ui::CallMuteButton> _mute;
object_ptr<Ui::CallButton> _hangup; object_ptr<Ui::CallButton> _hangup;
Fn<void()> _shareLinkCallback;
rpl::lifetime _peerLifetime; rpl::lifetime _peerLifetime;

View file

@ -35,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_group_call.h" #include "data/data_group_call.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "core/application.h" #include "core/application.h"
#include "boxes/single_choice_box.h" #include "ui/boxes/single_choice_box.h"
#include "webrtc/webrtc_audio_input_tester.h" #include "webrtc/webrtc_audio_input_tester.h"
#include "webrtc/webrtc_media_devices.h" #include "webrtc/webrtc_media_devices.h"
#include "settings/settings_common.h" #include "settings/settings_common.h"
@ -149,8 +149,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
} }
text.append(error.first); text.append(error.first);
if (const auto weak = *box) { if (const auto weak = *box) {
weak->getDelegate()->show( weak->getDelegate()->show(ConfirmBox({ .text = text }));
Box(ConfirmBox, text, nullptr, nullptr));
} }
return; return;
} }
@ -677,7 +676,7 @@ std::pair<Fn<void()>, rpl::lifetime> ShareInviteLinkAction(
return true; return true;
}; };
auto callback = [=] { auto callback = [=] {
const auto real = peer->groupCall(); const auto real = peer->migrateToOrMe()->groupCall();
if (shareReady() || state->generatingLink || !real) { if (shareReady() || state->generatingLink || !real) {
return; return;
} }
@ -702,11 +701,11 @@ std::pair<Fn<void()>, rpl::lifetime> ShareInviteLinkAction(
state->linkSpeakerRequestId = peer->session().api().request( state->linkSpeakerRequestId = peer->session().api().request(
MTPphone_ExportGroupCallInvite( MTPphone_ExportGroupCallInvite(
MTP_flags(Flag::f_can_self_unmute), MTP_flags(Flag::f_can_self_unmute),
real->input() real->input())
)).done([=](const MTPphone_ExportedGroupCallInvite &result) { ).done([=](const MTPphone_ExportedGroupCallInvite &result) {
state->linkSpeakerRequestId = 0; state->linkSpeakerRequestId = 0;
result.match([&]( result.match([&](
const MTPDphone_exportedGroupCallInvite &data) { const MTPDphone_exportedGroupCallInvite &data) {
state->linkSpeaker = qs(data.vlink()); state->linkSpeaker = qs(data.vlink());
shareReady(); shareReady();
}); });

View file

@ -374,7 +374,7 @@ void Instance::handleCallUpdate(
const MTPPhoneCall &call) { const MTPPhoneCall &call) {
if (call.type() == mtpc_phoneCallRequested) { if (call.type() == mtpc_phoneCallRequested) {
auto &phoneCall = call.c_phoneCallRequested(); auto &phoneCall = call.c_phoneCallRequested();
auto user = session->data().userLoaded(phoneCall.vadmin_id().v); auto user = session->data().userLoaded(phoneCall.vadmin_id());
if (!user) { if (!user) {
LOG(("API Error: User not loaded for phoneCallRequested.")); LOG(("API Error: User not loaded for phoneCallRequested."));
} else if (user->isSelf()) { } else if (user->isSelf()) {
@ -411,6 +411,14 @@ void Instance::handleCallUpdate(
void Instance::handleGroupCallUpdate( void Instance::handleGroupCallUpdate(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const MTPUpdate &update) { const MTPUpdate &update) {
if (_currentGroupCall
&& (&_currentGroupCall->peer()->session() == session)) {
update.match([&](const MTPDupdateGroupCall &data) {
_currentGroupCall->handlePossibleCreateOrJoinResponse(data);
}, [](const auto &) {
});
}
const auto callId = update.match([](const MTPDupdateGroupCall &data) { const auto callId = update.match([](const MTPDupdateGroupCall &data) {
return data.vcall().match([](const auto &data) { return data.vcall().match([](const auto &data) {
return data.vid().v; return data.vid().v;
@ -427,14 +435,6 @@ void Instance::handleGroupCallUpdate(
} else { } else {
applyGroupCallUpdateChecked(session, update); applyGroupCallUpdateChecked(session, update);
} }
if (_currentGroupCall
&& (&_currentGroupCall->peer()->session() == session)) {
update.match([&](const MTPDupdateGroupCall &data) {
_currentGroupCall->handlePossibleCreateOrJoinResponse(data);
}, [](const auto &) {
});
}
} }
void Instance::applyGroupCallUpdateChecked( void Instance::applyGroupCallUpdateChecked(

View file

@ -57,8 +57,13 @@ constexpr auto kHideBlobsDuration = crl::time(500);
constexpr auto kBlobLevelDuration = crl::time(250); constexpr auto kBlobLevelDuration = crl::time(250);
constexpr auto kBlobUpdateInterval = crl::time(100); constexpr auto kBlobUpdateInterval = crl::time(100);
auto BarStateFromMuteState(MuteState state, GroupCall::InstanceState instanceState) { auto BarStateFromMuteState(
return (instanceState == GroupCall::InstanceState::Disconnected) MuteState state,
GroupCall::InstanceState instanceState,
TimeId scheduledDate) {
return scheduledDate
? BarState::ForceMuted
: (instanceState == GroupCall::InstanceState::Disconnected)
? BarState::Connecting ? BarState::Connecting
: (state == MuteState::ForceMuted || state == MuteState::RaisedHand) : (state == MuteState::ForceMuted || state == MuteState::RaisedHand)
? BarState::ForceMuted ? BarState::ForceMuted
@ -293,19 +298,27 @@ void TopBar::initControls() {
_call _call
? mapToState(_call->muted()) ? mapToState(_call->muted())
: _groupCall->muted(), : _groupCall->muted(),
GroupCall::InstanceState::Connected)); GroupCall::InstanceState::Connected,
_call ? TimeId(0) : _groupCall->scheduleDate()));
using namespace rpl::mappers; using namespace rpl::mappers;
auto muted = _call auto muted = _call
? rpl::combine( ? rpl::combine(
_call->mutedValue() | rpl::map(mapToState), _call->mutedValue() | rpl::map(mapToState),
rpl::single(GroupCall::InstanceState::Connected) rpl::single(GroupCall::InstanceState::Connected),
rpl::single(TimeId(0))
) | rpl::type_erased() ) | rpl::type_erased()
: rpl::combine( : rpl::combine(
(_groupCall->mutedValue() (_groupCall->mutedValue()
| MapPushToTalkToActive() | MapPushToTalkToActive()
| rpl::distinct_until_changed() | rpl::distinct_until_changed()
| rpl::type_erased()), | rpl::type_erased()),
_groupCall->instanceStateValue() _groupCall->instanceStateValue(),
rpl::single(
_groupCall->scheduleDate()
) | rpl::then(_groupCall->real(
) | rpl::map([](not_null<Data::GroupCall*> call) {
return call->scheduleDateValue();
}) | rpl::flatten_latest())
) | rpl::filter(_2 != GroupCall::InstanceState::TransitionToRtc); ) | rpl::filter(_2 != GroupCall::InstanceState::TransitionToRtc);
std::move( std::move(
muted muted

View file

@ -399,7 +399,7 @@ EmojiListWidget::EmojiListWidget(
for (auto i = 0; i != kEmojiSectionCount; ++i) { for (auto i = 0; i != kEmojiSectionCount; ++i) {
const auto section = static_cast<Section>(i); const auto section = static_cast<Section>(i);
_counts[i] = (section == Section::Recent) _counts[i] = (section == Section::Recent)
? GetRecentEmoji().size() ? int(Core::App().settings().recentEmoji().size())
: Ui::Emoji::GetSectionCount(section); : Ui::Emoji::GetSectionCount(section);
} }
@ -500,17 +500,18 @@ void EmojiListWidget::ensureLoaded(int section) {
return; return;
} }
_emoji[section] = (static_cast<Section>(section) == Section::Recent) _emoji[section] = (static_cast<Section>(section) == Section::Recent)
? GetRecentEmojiSection() ? Core::App().settings().recentEmojiSection()
: Ui::Emoji::GetSection(static_cast<Section>(section)); : Ui::Emoji::GetSection(static_cast<Section>(section));
_counts[section] = _emoji[section].size(); _counts[section] = _emoji[section].size();
if (static_cast<Section>(section) == Section::Recent) { if (static_cast<Section>(section) == Section::Recent) {
return; return;
} }
const auto &variants = Core::App().settings().emojiVariants();
for (auto &emoji : _emoji[section]) { for (auto &emoji : _emoji[section]) {
if (emoji->hasVariants()) { if (emoji->hasVariants()) {
auto j = cEmojiVariants().constFind(emoji->nonColoredId()); const auto j = variants.find(emoji->nonColoredId());
if (j != cEmojiVariants().cend()) { if (j != end(variants)) {
emoji = emoji->variant(j.value()); emoji = emoji->variant(j->second);
} }
} }
} }
@ -594,10 +595,13 @@ void EmojiListWidget::mousePressEvent(QMouseEvent *e) {
if (_selected >= 0) { if (_selected >= 0) {
auto section = (_selected / MatrixRowShift); auto section = (_selected / MatrixRowShift);
auto sel = _selected % MatrixRowShift; auto sel = _selected % MatrixRowShift;
if (section < kEmojiSectionCount && sel < _emoji[section].size() && _emoji[section][sel]->hasVariants()) { if (section < kEmojiSectionCount
&& sel < _emoji[section].size()
&& _emoji[section][sel]->hasVariants()) {
_pickerSel = _selected; _pickerSel = _selected;
setCursor(style::cur_default); setCursor(style::cur_default);
if (!cEmojiVariants().contains(_emoji[section][sel]->nonColoredId())) { const auto &variants = Core::App().settings().emojiVariants();
if (!variants.contains(_emoji[section][sel]->nonColoredId())) {
showPicker(); showPicker();
} else { } else {
_showPickerTimer.callOnce(500); _showPickerTimer.callOnce(500);
@ -617,8 +621,11 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
} else if (_pickerSel >= 0) { } else if (_pickerSel >= 0) {
auto section = (_pickerSel / MatrixRowShift); auto section = (_pickerSel / MatrixRowShift);
auto sel = _pickerSel % MatrixRowShift; auto sel = _pickerSel % MatrixRowShift;
if (section < kEmojiSectionCount && sel < _emoji[section].size() && _emoji[section][sel]->hasVariants()) { if (section < kEmojiSectionCount
if (cEmojiVariants().contains(_emoji[section][sel]->nonColoredId())) { && sel < _emoji[section].size()
&& _emoji[section][sel]->hasVariants()) {
const auto &variants = Core::App().settings().emojiVariants();
if (variants.contains(_emoji[section][sel]->nonColoredId())) {
_picker->hideAnimated(); _picker->hideAnimated();
_pickerSel = -1; _pickerSel = -1;
} }
@ -650,7 +657,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
} }
void EmojiListWidget::selectEmoji(EmojiPtr emoji) { void EmojiListWidget::selectEmoji(EmojiPtr emoji) {
AddRecentEmoji(emoji); Core::App().settings().incrementRecentEmoji(emoji);
_chosen.fire_copy(emoji); _chosen.fire_copy(emoji);
} }
@ -698,10 +705,7 @@ QRect EmojiListWidget::emojiRect(int section, int sel) {
void EmojiListWidget::colorChosen(EmojiPtr emoji) { void EmojiListWidget::colorChosen(EmojiPtr emoji) {
if (emoji->hasVariants()) { if (emoji->hasVariants()) {
cRefEmojiVariants().insert( Core::App().settings().saveEmojiVariant(emoji);
emoji->nonColoredId(),
emoji->variantIndex(emoji));
controller()->session().saveSettingsDelayed();
} }
if (_pickerSel >= 0) { if (_pickerSel >= 0) {
auto section = (_pickerSel / MatrixRowShift); auto section = (_pickerSel / MatrixRowShift);
@ -790,7 +794,7 @@ void EmojiListWidget::processHideFinished() {
void EmojiListWidget::refreshRecent() { void EmojiListWidget::refreshRecent() {
clearSelection(); clearSelection();
_emoji[0] = GetRecentEmojiSection(); _emoji[0] = Core::App().settings().recentEmojiSection();
_counts[0] = _emoji[0].size(); _counts[0] = _emoji[0].size();
resizeToWidth(width()); resizeToWidth(width());
} }

View file

@ -116,11 +116,11 @@ auto SuggestionsWidget::getRowsByQuery() const -> std::vector<Row> {
}) | ranges::to_vector; }) | ranges::to_vector;
auto lastRecent = begin(result); auto lastRecent = begin(result);
const auto &recent = GetRecentEmoji(); const auto &recent = Core::App().settings().recentEmoji();
for (const auto &item : recent) { for (const auto &item : recent) {
const auto emoji = item.first->original() const auto emoji = item.emoji->original()
? item.first->original() ? item.emoji->original()
: item.first; : item.emoji;
const auto it = ranges::find(result, emoji, [](const Row &row) { const auto it = ranges::find(result, emoji, [](const Row &row) {
return row.emoji.get(); return row.emoji.get();
}); });
@ -133,12 +133,12 @@ auto SuggestionsWidget::getRowsByQuery() const -> std::vector<Row> {
for (auto &item : result) { for (auto &item : result) {
item.emoji = [&] { item.emoji = [&] {
const auto result = item.emoji; const auto result = item.emoji;
const auto &variants = cEmojiVariants(); const auto &variants = Core::App().settings().emojiVariants();
const auto i = result->hasVariants() const auto i = result->hasVariants()
? variants.constFind(result->nonColoredId()) ? variants.find(result->nonColoredId())
: variants.cend(); : end(variants);
return (i != variants.cend()) return (i != end(variants))
? result->variant(i.value()) ? result->variant(i->second)
: result.get(); : result.get();
}(); }();
} }

View file

@ -93,7 +93,7 @@ QString FieldTagMimeProcessor::tagFromMimeTag(const QString &mimeTag) {
const auto userId = _controller->session().userId(); const auto userId = _controller->session().userId();
auto match = QRegularExpression(":(\\d+)$").match(mimeTag); auto match = QRegularExpression(":(\\d+)$").match(mimeTag);
if (!match.hasMatch() if (!match.hasMatch()
|| match.capturedRef(1).toInt() != userId) { || match.capturedRef(1).toULongLong() != userId.bare) {
return QString(); return QString();
} }
return mimeTag.mid(0, mimeTag.size() - match.capturedLength()); return mimeTag.mid(0, mimeTag.size() - match.capturedLength());
@ -249,7 +249,7 @@ TextWithEntities StripSupportHashtag(TextWithEntities &&text) {
QString PrepareMentionTag(not_null<UserData*> user) { QString PrepareMentionTag(not_null<UserData*> user) {
return TextUtilities::kMentionTagStart return TextUtilities::kMentionTagStart
+ QString::number(user->bareId()) + QString::number(user->id.value)
+ '.' + '.'
+ QString::number(user->accessHash()); + QString::number(user->accessHash());
} }

View file

@ -14,9 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
enum { enum {
MaxSelectedItems = 100, MaxSelectedItems = 100,
MaxPhoneCodeLength = 4, // max length of country phone code
MaxPhoneTailLength = 32, // rest of the phone number, without country code (seen 12 at least), need more for service numbers
LocalEncryptIterCount = 4000, // key derivation iteration count LocalEncryptIterCount = 4000, // key derivation iteration count
LocalEncryptNoPwdIterCount = 4, // key derivation iteration count without pwd (not secure anyway) LocalEncryptNoPwdIterCount = 4, // key derivation iteration count without pwd (not secure anyway)
LocalEncryptSaltSize = 32, // 256 bit LocalEncryptSaltSize = 32, // 256 bit

View file

@ -63,6 +63,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_domain.h" #include "storage/storage_domain.h"
#include "storage/storage_databases.h" #include "storage/storage_databases.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
#include "payments/payments_checkout_process.h"
#include "export/export_manager.h" #include "export/export_manager.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "window/window_controller.h" #include "window/window_controller.h"
@ -169,6 +170,15 @@ Application::~Application() {
_window = nullptr; _window = nullptr;
_mediaView = nullptr; _mediaView = nullptr;
_notifications->clearAllFast(); _notifications->clearAllFast();
// We must manually destroy all windows before going further.
// DestroyWindow on Windows (at least with an active WebView) enters
// event loop and invoke scheduled crl::on_main callbacks.
//
// For example Domain::removeRedundantAccounts() is called from
// Domain::finish() and there is a violation on Ensures(started()).
Payments::CheckoutProcess::ClearAll();
_domain->finish(); _domain->finish();
Local::finish(); Local::finish();
@ -550,6 +560,9 @@ void Application::badMtprotoConfigurationError() {
void Application::startLocalStorage() { void Application::startLocalStorage() {
Local::start(); Local::start();
_saveSettingsTimer.emplace([=] { saveSettings(); }); _saveSettingsTimer.emplace([=] { saveSettings(); });
_settings.saveDelayedRequests() | rpl::start_with_next([=] {
saveSettingsDelayed();
}, _lifetime);
} }
void Application::startEmojiImageLoader() { void Application::startEmojiImageLoader() {

View file

@ -21,17 +21,20 @@ void BaseIntegration::enterFromEventLoop(FnMut<void()> &&method) {
std::move(method)); std::move(method));
} }
bool BaseIntegration::logSkipDebug() {
return !Logs::DebugEnabled() && Logs::started();
}
void BaseIntegration::logMessageDebug(const QString &message) {
Logs::writeDebug(message);
}
void BaseIntegration::logMessage(const QString &message) { void BaseIntegration::logMessage(const QString &message) {
#ifdef DEBUG_LOG Logs::writeMain(message);
DEBUG_LOG((message));
#endif // DEBUG_LOG
} }
void BaseIntegration::logAssertionViolation(const QString &info) { void BaseIntegration::logAssertionViolation(const QString &info) {
#ifdef LOG Logs::writeMain("Assertion Failed! " + info);
LOG(("Assertion Failed! ") + info);
#endif // LOG
CrashReports::SetAnnotation("Assertion", info); CrashReports::SetAnnotation("Assertion", info);
} }

View file

@ -11,11 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Core { namespace Core {
class BaseIntegration : public base::Integration { class BaseIntegration final : public base::Integration {
public: public:
BaseIntegration(int argc, char *argv[]); BaseIntegration(int argc, char *argv[]);
void enterFromEventLoop(FnMut<void()> &&method) override; void enterFromEventLoop(FnMut<void()> &&method) override;
bool logSkipDebug() override;
void logMessageDebug(const QString &message) override;
void logMessage(const QString &message) override; void logMessage(const QString &message) override;
void logAssertionViolation(const QString &info) override; void logAssertionViolation(const QString &info) override;
[[nodiscard]] bool gtkIntegrationEnabled() const override; [[nodiscard]] bool gtkIntegrationEnabled() const override;

View file

@ -87,12 +87,12 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const {
open(); open();
} else if (!_bot } else if (!_bot
|| _bot->isVerified() || _bot->isVerified()
|| _bot->session().local().isBotTrusted(_bot)) { || _bot->session().local().isBotTrustedOpenGame(_bot->id)) {
open(); open();
} else { } else {
const auto callback = [=, bot = _bot] { const auto callback = [=, bot = _bot] {
Ui::hideLayer(); Ui::hideLayer();
bot->session().local().markBotTrusted(bot); bot->session().local().markBotTrustedOpenGame(bot->id);
open(); open();
}; };
Ui::show(Box<ConfirmBox>( Ui::show(Box<ConfirmBox>(
@ -137,7 +137,9 @@ void MentionNameClickHandler::onClick(ClickContext context) const {
} }
auto MentionNameClickHandler::getTextEntity() const -> TextEntity { auto MentionNameClickHandler::getTextEntity() const -> TextEntity {
auto data = QString::number(_userId) + '.' + QString::number(_accessHash); const auto data = QString::number(_userId.bare)
+ '.'
+ QString::number(_accessHash);
return { EntityType::MentionName, data }; return { EntityType::MentionName, data };
} }

View file

@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Core { namespace Core {
namespace { namespace {
constexpr auto kRecentEmojiLimit = 42;
[[nodiscard]] WindowPosition Deserialize(const QByteArray &data) { [[nodiscard]] WindowPosition Deserialize(const QByteArray &data) {
QDataStream stream(data); QDataStream stream(data);
stream.setVersion(QDataStream::Qt_5_1); stream.setVersion(QDataStream::Qt_5_1);
@ -75,6 +77,17 @@ QByteArray Settings::serialize() const {
const auto themesAccentColors = _themesAccentColors.serialize(); const auto themesAccentColors = _themesAccentColors.serialize();
const auto windowPosition = Serialize(_windowPosition); const auto windowPosition = Serialize(_windowPosition);
auto recentEmojiPreloadGenerated = std::vector<RecentEmojiId>();
if (_recentEmojiPreload.empty()) {
recentEmojiPreloadGenerated.reserve(_recentEmoji.size());
for (const auto [emoji, rating] : _recentEmoji) {
recentEmojiPreloadGenerated.push_back({ emoji->id(), rating });
}
}
const auto &recentEmojiPreloadData = _recentEmojiPreload.empty()
? recentEmojiPreloadGenerated
: _recentEmojiPreload;
auto size = Serialize::bytearraySize(themesAccentColors) auto size = Serialize::bytearraySize(themesAccentColors)
+ sizeof(qint32) * 5 + sizeof(qint32) * 5
+ Serialize::stringSize(_downloadPath.current()) + Serialize::stringSize(_downloadPath.current())
@ -83,10 +96,16 @@ QByteArray Settings::serialize() const {
+ Serialize::stringSize(_callOutputDeviceId) + Serialize::stringSize(_callOutputDeviceId)
+ Serialize::stringSize(_callInputDeviceId) + Serialize::stringSize(_callInputDeviceId)
+ Serialize::stringSize(_callVideoInputDeviceId) + Serialize::stringSize(_callVideoInputDeviceId)
+ sizeof(qint32) * 3; + sizeof(qint32) * 5;
for (const auto &[key, value] : _soundOverrides) { for (const auto &[key, value] : _soundOverrides) {
size += Serialize::stringSize(key) + Serialize::stringSize(value); size += Serialize::stringSize(key) + Serialize::stringSize(value);
} }
for (const auto &[id, rating] : recentEmojiPreloadData) {
size += Serialize::stringSize(id) + sizeof(quint16);
}
for (const auto &[id, variant] : _emojiVariants) {
size += Serialize::stringSize(id) + sizeof(quint8);
}
size += Serialize::bytearraySize(_videoPipGeometry); size += Serialize::bytearraySize(_videoPipGeometry);
size += Serialize::bytearraySize(windowPosition); size += Serialize::bytearraySize(windowPosition);
@ -165,7 +184,16 @@ QByteArray Settings::serialize() const {
<< qint64(_groupCallPushToTalkDelay) << qint64(_groupCallPushToTalkDelay)
<< qint32(0) // Call audio backend << qint32(0) // Call audio backend
<< qint32(_disableCalls ? 1 : 0) << qint32(_disableCalls ? 1 : 0)
<< windowPosition; << windowPosition
<< qint32(recentEmojiPreloadData.size());
for (const auto &[id, rating] : recentEmojiPreloadData) {
stream << id << quint16(rating);
}
stream
<< qint32(_emojiVariants.size());
for (const auto &[id, variant] : _emojiVariants) {
stream << id << quint8(variant);
}
} }
return result; return result;
} }
@ -239,6 +267,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
qint32 callAudioBackend = 0; qint32 callAudioBackend = 0;
qint32 disableCalls = _disableCalls ? 1 : 0; qint32 disableCalls = _disableCalls ? 1 : 0;
QByteArray windowPosition; QByteArray windowPosition;
std::vector<RecentEmojiId> recentEmojiPreload;
base::flat_map<QString, uint8> emojiVariants;
stream >> themesAccentColors; stream >> themesAccentColors;
if (!stream.atEnd()) { if (!stream.atEnd()) {
@ -343,6 +373,30 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) { if (!stream.atEnd()) {
stream >> windowPosition; stream >> windowPosition;
} }
if (!stream.atEnd()) {
auto recentCount = qint32(0);
stream >> recentCount;
if (recentCount > 0 && recentCount < 10000) {
recentEmojiPreload.reserve(recentCount);
for (auto i = 0; i != recentCount; ++i) {
auto id = QString();
auto rating = quint16();
stream >> id >> rating;
recentEmojiPreload.push_back({ id, rating });
}
}
auto variantsCount = qint32(0);
stream >> variantsCount;
if (variantsCount > 0 && variantsCount < 10000) {
emojiVariants.reserve(variantsCount);
for (auto i = 0; i != variantsCount; ++i) {
auto id = QString();
auto variant = quint8();
stream >> id >> variant;
emojiVariants.emplace(id, variant);
}
}
}
if (stream.status() != QDataStream::Ok) { if (stream.status() != QDataStream::Ok) {
LOG(("App Error: " LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()")); "Bad data for Core::Settings::constructFromSerialized()"));
@ -446,6 +500,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!windowPosition.isEmpty()) { if (!windowPosition.isEmpty()) {
_windowPosition = Deserialize(windowPosition); _windowPosition = Deserialize(windowPosition);
} }
_recentEmojiPreload = std::move(recentEmojiPreload);
_emojiVariants = std::move(emojiVariants);
} }
bool Settings::chatWide() const { bool Settings::chatWide() const {
@ -525,6 +581,118 @@ rpl::producer<int> Settings::thirdColumnWidthChanges() const {
return _thirdColumnWidth.changes(); return _thirdColumnWidth.changes();
} }
const std::vector<Settings::RecentEmoji> &Settings::recentEmoji() const {
if (_recentEmoji.empty()) {
resolveRecentEmoji();
}
return _recentEmoji;
}
void Settings::resolveRecentEmoji() const {
const auto haveAlready = [&](EmojiPtr emoji) {
return ranges::contains(
_recentEmoji,
emoji->id(),
[](const RecentEmoji &data) { return data.emoji->id(); });
};
if (!_recentEmojiPreload.empty()) {
_recentEmoji.reserve(_recentEmojiPreload.size());
for (const auto &[id, rating] : base::take(_recentEmojiPreload)) {
if (const auto emoji = Ui::Emoji::Find(id)) {
if (!haveAlready(emoji)) {
_recentEmoji.push_back({ emoji, rating });
}
}
}
_recentEmojiPreload.clear();
}
for (const auto emoji : Ui::Emoji::GetDefaultRecent()) {
if (_recentEmoji.size() >= kRecentEmojiLimit) {
break;
} else if (!haveAlready(emoji)) {
_recentEmoji.push_back({ emoji, 1 });
}
}
}
EmojiPack Settings::recentEmojiSection() const {
const auto &recent = recentEmoji();
auto result = EmojiPack();
result.reserve(recent.size());
for (const auto [emoji, rating] : recent) {
result.push_back(emoji);
}
return result;
}
void Settings::incrementRecentEmoji(EmojiPtr emoji) {
resolveRecentEmoji();
auto i = _recentEmoji.begin(), e = _recentEmoji.end();
for (; i != e; ++i) {
if (i->emoji == emoji) {
++i->rating;
if (i->rating > 0x8000) {
for (auto j = _recentEmoji.begin(); j != e; ++j) {
if (j->rating > 1) {
j->rating /= 2;
} else {
j->rating = 1;
}
}
}
for (; i != _recentEmoji.begin(); --i) {
if ((i - 1)->rating > i->rating) {
break;
}
std::swap(*i, *(i - 1));
}
break;
}
}
if (i == e) {
while (_recentEmoji.size() >= kRecentEmojiLimit) {
_recentEmoji.pop_back();
}
_recentEmoji.push_back({ emoji, 1 });
for (i = _recentEmoji.end() - 1; i != _recentEmoji.begin(); --i) {
if ((i - 1)->rating > i->rating) {
break;
}
std::swap(*i, *(i - 1));
}
}
_recentEmojiUpdated.fire({});
_saveDelayed.fire({});
}
void Settings::setLegacyRecentEmojiPreload(
QVector<QPair<QString, ushort>> data) {
if (!_recentEmojiPreload.empty() || data.isEmpty()) {
return;
}
_recentEmojiPreload.reserve(data.size());
for (const auto &[id, rating] : data) {
_recentEmojiPreload.push_back({ id, rating });
}
}
void Settings::saveEmojiVariant(EmojiPtr emoji) {
_emojiVariants[emoji->nonColoredId()] = emoji->variantIndex(emoji);
_saveDelayed.fire({});
}
void Settings::setLegacyEmojiVariants(QMap<QString, int> data) {
if (!_emojiVariants.empty() || data.isEmpty()) {
return;
}
_emojiVariants.reserve(data.size());
for (auto i = data.begin(), e = data.end(); i != e; ++i) {
_emojiVariants.emplace(i.key(), i.value());
}
}
void Settings::resetOnLastLogout() { void Settings::resetOnLastLogout() {
_adaptiveForWide = true; _adaptiveForWide = true;
_moderateModeEnabled = false; _moderateModeEnabled = false;
@ -592,6 +760,10 @@ void Settings::resetOnLastLogout() {
_notifyFromAll = true; _notifyFromAll = true;
_tabbedReplacedWithInfo = false; // per-window _tabbedReplacedWithInfo = false; // per-window
_systemDarkModeEnabled = false; _systemDarkModeEnabled = false;
_recentEmojiPreload.clear();
_recentEmoji.clear();
_emojiVariants.clear();
} }
bool Settings::ThirdColumnByDefault() { bool Settings::ThirdColumnByDefault() {

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/themes/window_themes_embedded.h" #include "window/themes/window_themes_embedded.h"
#include "ui/chat/attach/attach_send_files_way.h" #include "ui/chat/attach/attach_send_files_way.h"
#include "platform/platform_notifications_manager.h" #include "platform/platform_notifications_manager.h"
#include "emoji.h"
enum class RectPart; enum class RectPart;
@ -53,6 +54,10 @@ public:
Settings(); Settings();
[[nodiscard]] rpl::producer<> saveDelayedRequests() const {
return _saveDelayed.events();
}
[[nodiscard]] static bool IsLeftCorner(ScreenCorner corner) { [[nodiscard]] static bool IsLeftCorner(ScreenCorner corner) {
return (corner == ScreenCorner::TopLeft) return (corner == ScreenCorner::TopLeft)
|| (corner == ScreenCorner::BottomLeft); || (corner == ScreenCorner::BottomLeft);
@ -510,8 +515,26 @@ public:
_windowPosition = position; _windowPosition = position;
} }
struct RecentEmoji {
EmojiPtr emoji = nullptr;
ushort rating = 0;
};
[[nodiscard]] const std::vector<RecentEmoji> &recentEmoji() const;
[[nodiscard]] EmojiPack recentEmojiSection() const;
void incrementRecentEmoji(EmojiPtr emoji);
void setLegacyRecentEmojiPreload(QVector<QPair<QString, ushort>> data);
[[nodiscard]] rpl::producer<> recentEmojiUpdated() const {
return _recentEmojiUpdated.events();
}
[[nodiscard]] const base::flat_map<QString, uint8> &emojiVariants() const {
return _emojiVariants;
}
void saveEmojiVariant(EmojiPtr emoji);
void setLegacyEmojiVariants(QMap<QString, int> data);
[[nodiscard]] static bool ThirdColumnByDefault(); [[nodiscard]] static bool ThirdColumnByDefault();
[[nodiscard]] float64 DefaultDialogsWidthRatio(); [[nodiscard]] static float64 DefaultDialogsWidthRatio();
[[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) { [[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) {
return int(std::round(std::clamp(speed, 0.5, 2.0) * 100)); return int(std::round(std::clamp(speed, 0.5, 2.0) * 100));
} }
@ -527,10 +550,17 @@ public:
void resetOnLastLogout(); void resetOnLastLogout();
private: private:
void resolveRecentEmoji() const;
static constexpr auto kDefaultThirdColumnWidth = 0; static constexpr auto kDefaultThirdColumnWidth = 0;
static constexpr auto kDefaultDialogsWidthRatio = 5. / 14; static constexpr auto kDefaultDialogsWidthRatio = 5. / 14;
static constexpr auto kDefaultBigDialogsWidthRatio = 0.275; static constexpr auto kDefaultBigDialogsWidthRatio = 0.275;
struct RecentEmojiId {
QString emoji;
ushort rating = 0;
};
bool _adaptiveForWide = true; bool _adaptiveForWide = true;
bool _moderateModeEnabled = false; bool _moderateModeEnabled = false;
rpl::variable<float64> _songVolume = kDefaultVolume; rpl::variable<float64> _songVolume = kDefaultVolume;
@ -578,6 +608,10 @@ private:
rpl::variable<std::vector<int>> _dictionariesEnabled; rpl::variable<std::vector<int>> _dictionariesEnabled;
rpl::variable<bool> _autoDownloadDictionaries = true; rpl::variable<bool> _autoDownloadDictionaries = true;
rpl::variable<bool> _mainMenuAccountsShown = true; rpl::variable<bool> _mainMenuAccountsShown = true;
mutable std::vector<RecentEmojiId> _recentEmojiPreload;
mutable std::vector<RecentEmoji> _recentEmoji;
base::flat_map<QString, uint8> _emojiVariants;
rpl::event_stream<> _recentEmojiUpdated;
bool _tabbedSelectorSectionEnabled = false; // per-window bool _tabbedSelectorSectionEnabled = false; // per-window
Window::Column _floatPlayerColumn = Window::Column(); // per-window Window::Column _floatPlayerColumn = Window::Column(); // per-window
RectPart _floatPlayerCorner = RectPart(); // per-window RectPart _floatPlayerCorner = RectPart(); // per-window
@ -595,6 +629,7 @@ private:
bool _tabbedReplacedWithInfo = false; // per-window bool _tabbedReplacedWithInfo = false; // per-window
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window
rpl::event_stream<> _saveDelayed;
float64 _rememberedSongVolume = kDefaultVolume; float64 _rememberedSongVolume = kDefaultVolume;
bool _rememberedSoundNotifyFromTray = false; bool _rememberedSoundNotifyFromTray = false;
bool _rememberedFlashBounceNotifyFromTray = false; bool _rememberedFlashBounceNotifyFromTray = false;

View file

@ -283,19 +283,7 @@ void Launcher::init() {
_arguments = readArguments(_argc, _argv); _arguments = readArguments(_argc, _argv);
prepareSettings(); prepareSettings();
initQtMessageLogging();
static QtMessageHandler originalMessageHandler = nullptr;
originalMessageHandler = qInstallMessageHandler([](
QtMsgType type,
const QMessageLogContext &context,
const QString &msg) {
if (originalMessageHandler) {
originalMessageHandler(type, context, msg);
}
if (Logs::DebugEnabled() || !Logs::started()) {
LOG((msg));
}
});
QApplication::setApplicationName(qsl("KotatogramDesktop")); QApplication::setApplicationName(qsl("KotatogramDesktop"));
@ -451,6 +439,26 @@ void Launcher::prepareSettings() {
processArguments(); processArguments();
} }
void Launcher::initQtMessageLogging() {
static QtMessageHandler OriginalMessageHandler = nullptr;
static bool WritingQtMessage = false;
OriginalMessageHandler = qInstallMessageHandler([](
QtMsgType type,
const QMessageLogContext &context,
const QString &msg) {
if (OriginalMessageHandler) {
OriginalMessageHandler(type, context, msg);
}
if (Logs::DebugEnabled() || !Logs::started()) {
if (!WritingQtMessage) {
WritingQtMessage = true;
LOG((msg));
WritingQtMessage = false;
}
}
});
}
uint64 Launcher::installationTag() const { uint64 Launcher::installationTag() const {
return InstallationTag; return InstallationTag;
} }

View file

@ -41,6 +41,7 @@ protected:
private: private:
void prepareSettings(); void prepareSettings();
void initQtMessageLogging();
void processArguments(); void processArguments();
QStringList readArguments(int argc, char *argv[]) const; QStringList readArguments(int argc, char *argv[]) const;

View file

@ -192,7 +192,7 @@ bool ShowPassportForm(
if (!controller) { if (!controller) {
return false; return false;
} }
const auto botId = params.value("bot_id", QString()).toInt(); const auto botId = params.value("bot_id", QString()).toULongLong();
const auto scope = params.value("scope", QString()); const auto scope = params.value("scope", QString());
const auto callback = params.value("callback_url", QString()); const auto callback = params.value("callback_url", QString());
const auto publicKey = params.value("public_key", QString()); const auto publicKey = params.value("public_key", QString());
@ -323,7 +323,8 @@ bool ResolvePrivatePost(
const auto params = url_parse_params( const auto params = url_parse_params(
match->captured(1), match->captured(1),
qthelp::UrlParamNameTransform::ToLower); qthelp::UrlParamNameTransform::ToLower);
const auto channelId = params.value(qsl("channel")).toInt(); const auto channelId = ChannelId(
params.value(qsl("channel")).toULongLong());
const auto msgId = params.value(qsl("post")).toInt(); const auto msgId = params.value(qsl("post")).toInt();
const auto commentParam = params.value(qsl("comment")); const auto commentParam = params.value(qsl("comment"));
const auto commentId = commentParam.toInt(); const auto commentId = commentParam.toInt();

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