Updated TDesktop sources to 2.7.2
3
.gitattributes
vendored
|
|
@ -4,3 +4,6 @@
|
|||
# Ensure diffs have LF endings
|
||||
*.diff text eol=lf
|
||||
*.bat text eol=crlf
|
||||
|
||||
# Ensure lottie animations are treated as binary files
|
||||
*.lottie binary
|
||||
|
|
|
|||
2
.gitignore
vendored
|
|
@ -49,3 +49,5 @@ stage
|
|||
/Linux/
|
||||
/Telegram/Makefile
|
||||
*.*~
|
||||
.idea/
|
||||
cmake-build-debug/
|
||||
|
|
|
|||
3
.gitmodules
vendored
|
|
@ -88,3 +88,6 @@
|
|||
[submodule "Telegram/ThirdParty/tgcalls"]
|
||||
path = Telegram/ThirdParty/tgcalls
|
||||
url = https://github.com/TelegramMessenger/tgcalls.git
|
||||
[submodule "Telegram/lib_webview"]
|
||||
path = Telegram/lib_webview
|
||||
url = https://github.com/desktop-app/lib_webview.git
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ add_subdirectory(lib_storage)
|
|||
add_subdirectory(lib_lottie)
|
||||
add_subdirectory(lib_qr)
|
||||
add_subdirectory(lib_webrtc)
|
||||
add_subdirectory(lib_webview)
|
||||
add_subdirectory(codegen)
|
||||
|
||||
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/lib_ffmpeg.cmake)
|
||||
include(cmake/lib_stripe.cmake)
|
||||
include(cmake/lib_tgvoip.cmake)
|
||||
include(cmake/lib_tgcalls.cmake)
|
||||
include(cmake/td_export.cmake)
|
||||
|
|
@ -35,6 +37,11 @@ include(cmake/td_scheme.cmake)
|
|||
include(cmake/td_ui.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)
|
||||
|
||||
target_link_libraries(Telegram
|
||||
|
|
@ -55,7 +62,9 @@ PRIVATE
|
|||
desktop-app::lib_storage
|
||||
desktop-app::lib_lottie
|
||||
desktop-app::lib_qr
|
||||
desktop-app::lib_webview
|
||||
desktop-app::lib_ffmpeg
|
||||
desktop-app::lib_stripe
|
||||
desktop-app::external_lz4
|
||||
desktop-app::external_rlottie
|
||||
desktop-app::external_zlib
|
||||
|
|
@ -70,7 +79,12 @@ PRIVATE
|
|||
desktop-app::external_xxhash
|
||||
)
|
||||
|
||||
if (LINUX)
|
||||
if (WIN32)
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
desktop-app::lib_webview_winrt
|
||||
)
|
||||
elseif (LINUX)
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
desktop-app::external_glibmm
|
||||
|
|
@ -249,8 +263,6 @@ PRIVATE
|
|||
boxes/sessions_box.h
|
||||
boxes/share_box.cpp
|
||||
boxes/share_box.h
|
||||
boxes/single_choice_box.cpp
|
||||
boxes/single_choice_box.h
|
||||
boxes/sticker_set_box.cpp
|
||||
boxes/sticker_set_box.h
|
||||
boxes/stickers_box.cpp
|
||||
|
|
@ -383,8 +395,6 @@ PRIVATE
|
|||
data/data_cloud_file.h
|
||||
data/data_cloud_themes.cpp
|
||||
data/data_cloud_themes.h
|
||||
data/data_countries.cpp
|
||||
data/data_countries.h
|
||||
data/data_document.cpp
|
||||
data/data_document.h
|
||||
data/data_document_media.cpp
|
||||
|
|
@ -415,6 +425,8 @@ PRIVATE
|
|||
data/data_notify_settings.h
|
||||
data/data_peer.cpp
|
||||
data/data_peer.h
|
||||
data/data_peer_id.cpp
|
||||
data/data_peer_id.h
|
||||
data/data_peer_values.cpp
|
||||
data/data_peer_values.h
|
||||
data/data_photo.cpp
|
||||
|
|
@ -811,8 +823,6 @@ PRIVATE
|
|||
passport/passport_panel.h
|
||||
passport/passport_panel_controller.cpp
|
||||
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.h
|
||||
passport/passport_panel_edit_document.cpp
|
||||
|
|
@ -823,6 +833,10 @@ PRIVATE
|
|||
passport/passport_panel_form.h
|
||||
passport/passport_panel_password.cpp
|
||||
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.h
|
||||
platform/linux/linux_gdk_helper.cpp
|
||||
|
|
@ -1022,8 +1036,6 @@ PRIVATE
|
|||
ui/widgets/level_meter.h
|
||||
ui/widgets/multi_select.cpp
|
||||
ui/widgets/multi_select.h
|
||||
ui/widgets/separate_panel.cpp
|
||||
ui/widgets/separate_panel.h
|
||||
ui/countryinput.cpp
|
||||
ui/countryinput.h
|
||||
ui/empty_userpic.cpp
|
||||
|
|
@ -1039,8 +1051,6 @@ PRIVATE
|
|||
ui/search_field_controller.h
|
||||
ui/special_buttons.cpp
|
||||
ui/special_buttons.h
|
||||
ui/special_fields.cpp
|
||||
ui/special_fields.h
|
||||
ui/unread_badge.cpp
|
||||
ui/unread_badge.h
|
||||
window/main_window.cpp
|
||||
|
|
|
|||
BIN
Telegram/Resources/icons/calls/group_calls_share.png
Normal file
|
After Width: | Height: | Size: 566 B |
BIN
Telegram/Resources/icons/calls/group_calls_share@2x.png
Normal file
|
After Width: | Height: | Size: 1 KiB |
BIN
Telegram/Resources/icons/calls/group_calls_share@3x.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
1
Telegram/Resources/icons/calls/hands.lottie
Normal file
1
Telegram/Resources/icons/calls/voice.lottie
Normal file
BIN
Telegram/Resources/icons/inline_button_card.png
Normal file
|
After Width: | Height: | Size: 213 B |
BIN
Telegram/Resources/icons/inline_button_card@2x.png
Normal file
|
After Width: | Height: | Size: 249 B |
BIN
Telegram/Resources/icons/inline_button_card@3x.png
Normal file
|
After Width: | Height: | Size: 300 B |
|
Before Width: | Height: | Size: 228 B After Width: | Height: | Size: 338 B |
|
Before Width: | Height: | Size: 341 B After Width: | Height: | Size: 569 B |
|
Before Width: | Height: | Size: 427 B After Width: | Height: | Size: 768 B |
|
Before Width: | Height: | Size: 146 B After Width: | Height: | Size: 305 B |
|
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 428 B |
|
Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 550 B |
BIN
Telegram/Resources/icons/payments/payment_address.png
Normal file
|
After Width: | Height: | Size: 802 B |
BIN
Telegram/Resources/icons/payments/payment_address@2x.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Telegram/Resources/icons/payments/payment_address@3x.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
Telegram/Resources/icons/payments/payment_card.png
Normal file
|
After Width: | Height: | Size: 357 B |
BIN
Telegram/Resources/icons/payments/payment_card@2x.png
Normal file
|
After Width: | Height: | Size: 560 B |
BIN
Telegram/Resources/icons/payments/payment_card@3x.png
Normal file
|
After Width: | Height: | Size: 985 B |
BIN
Telegram/Resources/icons/payments/payment_email.png
Normal file
|
After Width: | Height: | Size: 949 B |
BIN
Telegram/Resources/icons/payments/payment_email@2x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
Telegram/Resources/icons/payments/payment_email@3x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
Telegram/Resources/icons/payments/payment_name.png
Normal file
|
After Width: | Height: | Size: 473 B |
BIN
Telegram/Resources/icons/payments/payment_name@2x.png
Normal file
|
After Width: | Height: | Size: 884 B |
BIN
Telegram/Resources/icons/payments/payment_name@3x.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/Resources/icons/payments/payment_phone.png
Normal file
|
After Width: | Height: | Size: 711 B |
BIN
Telegram/Resources/icons/payments/payment_phone@2x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/payments/payment_phone@3x.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
Telegram/Resources/icons/payments/payment_shipping.png
Normal file
|
After Width: | Height: | Size: 526 B |
BIN
Telegram/Resources/icons/payments/payment_shipping@2x.png
Normal file
|
After Width: | Height: | Size: 1,022 B |
BIN
Telegram/Resources/icons/payments/payment_shipping@3x.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
|
|
@ -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_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_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_calling" = "Requesting a call from Telegram...";
|
||||
"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_disable_filtering" = "Disable filtering";
|
||||
"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_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_users_and_one" = "{accumulated}, {user}";
|
||||
"lng_action_invite_users_and_last" = "{accumulated} and {user}";
|
||||
"lng_action_group_call_started" = "{from} started {chat}";
|
||||
"lng_action_group_call_started_chat" = "a voice chat";
|
||||
"lng_action_group_call_started_group" = "{from} started 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_add_user" = "{from} added {user}";
|
||||
"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_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_test" = "Test receipt";
|
||||
"lng_payments_invoice_label" = "Invoice";
|
||||
"lng_payments_invoice_label_test" = "Test invoice";
|
||||
"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_connecting" = "connecting...";
|
||||
|
|
@ -1927,6 +1994,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_group_call_inactive" = "listening";
|
||||
"lng_group_call_raised_hand_status" = "wants to speak";
|
||||
"lng_group_call_settings" = "Settings";
|
||||
"lng_group_call_share_button" = "Share";
|
||||
"lng_group_call_unmute" = "Unmute";
|
||||
"lng_group_call_unmute_sub" = "or hold spacebar to talk";
|
||||
"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_title" = "Leave 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_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?";
|
||||
|
|
@ -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_listener_link" = "Copy Listener Link";
|
||||
"lng_group_call_end" = "End Voice Chat";
|
||||
"lng_group_call_cancel" = "Abort Voice Chat";
|
||||
"lng_group_call_join" = "Join";
|
||||
"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.";
|
||||
|
|
@ -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_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_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_edit_title" = "Edit voice chat title";
|
||||
"lng_group_call_switch_done" = "Members of this voice chat will now see you as **{user}**";
|
||||
|
|
|
|||
|
|
@ -69,9 +69,8 @@
|
|||
<file alias="day-blue.tdesktop-theme">../../day-blue.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="icons/calls/active_hand.json">../../icons/calls/active_hand.json</file>
|
||||
<file alias="icons/calls/hand_muted_active.json">../../icons/calls/hand_muted_active.json</file>
|
||||
<file alias="icons/calls/raised_hand.json">../../icons/calls/raised_hand.json</file>
|
||||
<file alias="icons/calls/hands.lottie">../../icons/calls/hands.lottie</file>
|
||||
<file alias="icons/calls/voice.lottie">../../icons/calls/voice.lottie</file>
|
||||
</qresource>
|
||||
<qresource prefix="/qt-project.org">
|
||||
<file>../qmime/freedesktop.org.xml</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;
|
||||
inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = 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;
|
||||
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;
|
||||
|
|
@ -90,8 +90,8 @@ inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;
|
|||
inputTakeoutFileLocation#29be5899 = 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;
|
||||
inputPeerPhotoFileLocation#27d69997 flags:# big:flags.0?true peer:InputPeer volume_id:long local_id:int = InputFileLocation;
|
||||
inputStickerSetThumb#dbaeae9 stickerset:InputStickerSet volume_id:long local_id:int = InputFileLocation;
|
||||
inputPeerPhotoFileLocation#37257e99 flags:# big:flags.0?true peer:InputPeer photo_id:long = InputFileLocation;
|
||||
inputStickerSetThumb#9d84f3db stickerset:InputStickerSet thumb_version:int = InputFileLocation;
|
||||
inputGroupCallStream#bba51639 call:InputGroupCall time_ms:long scale:int = InputFileLocation;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
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;
|
||||
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;
|
||||
messageActionInviteToGroupCall#76b9f11a call:InputGroupCall users:Vector<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;
|
||||
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;
|
||||
|
||||
photoSizeEmpty#e17e23c type:string = PhotoSize;
|
||||
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
|
||||
photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
|
||||
photoSize#75c78e60 type:string w:int h:int size:int = PhotoSize;
|
||||
photoCachedSize#21e1ad6 type:string w:int h:int 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;
|
||||
|
||||
geoPointEmpty#1117dd5f = GeoPoint;
|
||||
|
|
@ -554,7 +555,7 @@ inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
|
|||
inputStickerSetAnimatedEmoji#28703c8 = 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;
|
||||
|
||||
|
|
@ -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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -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;
|
||||
|
||||
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.paymentResult#4e5f810d updates:Updates = 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;
|
||||
|
||||
|
|
@ -1088,8 +1091,6 @@ emojiURL#a575739d url:string = EmojiURL;
|
|||
|
||||
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;
|
||||
|
||||
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.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;
|
||||
|
||||
|
|
@ -1203,11 +1204,11 @@ peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;
|
|||
stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -1592,10 +1593,10 @@ bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
|||
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
||||
bots.setBotCommands#805d46f6 commands:Vector<BotCommand> = Bool;
|
||||
|
||||
payments.getPaymentForm#99f09745 msg_id:int = payments.PaymentForm;
|
||||
payments.getPaymentReceipt#a092a980 msg_id:int = payments.PaymentReceipt;
|
||||
payments.validateRequestedInfo#770a8e74 flags:# save:flags.0?true 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.getPaymentForm#8a333c8d flags:# peer:InputPeer msg_id:int theme_params:flags.0?DataJSON = payments.PaymentForm;
|
||||
payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt;
|
||||
payments.validateRequestedInfo#db103170 flags:# save:flags.0?true peer:InputPeer msg_id:int info:PaymentRequestedInfo = payments.ValidatedRequestedInfo;
|
||||
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.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool;
|
||||
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.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = 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.leaveGroupCall#500377f9 call:InputGroupCall source:int = 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.getGroupCallJoinAs#ef7c213a peer:InputPeer = phone.JoinAsPeers;
|
||||
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.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.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
|
||||
|
||||
// LAYER 126
|
||||
// LAYER 128
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="2.7.1.0" />
|
||||
Version="2.7.2.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
|||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,7,1,0
|
||||
PRODUCTVERSION 2,7,1,0
|
||||
FILEVERSION 2,7,2,0
|
||||
PRODUCTVERSION 2,7,2,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
|
@ -62,10 +62,10 @@ BEGIN
|
|||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "2.7.1.0"
|
||||
VALUE "FileVersion", "2.7.2.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.7.1.0"
|
||||
VALUE "ProductVersion", "2.7.2.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
|||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,7,1,0
|
||||
PRODUCTVERSION 2,7,1,0
|
||||
FILEVERSION 2,7,2,0
|
||||
PRODUCTVERSION 2,7,2,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
|
@ -53,10 +53,10 @@ BEGIN
|
|||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "2.7.1.0"
|
||||
VALUE "FileVersion", "2.7.2.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.7.1.0"
|
||||
VALUE "ProductVersion", "2.7.2.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ JoinedByLinkSlice ParseJoinedByLinkSlice(
|
|||
for (const auto &importer : data.vimporters().v) {
|
||||
importer.match([&](const MTPDchatInviteImporter &data) {
|
||||
result.users.push_back({
|
||||
.user = owner.user(data.vuser_id().v),
|
||||
.user = owner.user(data.vuser_id()),
|
||||
.date = data.vdate().v,
|
||||
});
|
||||
});
|
||||
|
|
@ -623,7 +623,7 @@ auto InviteLinks::parse(
|
|||
return invite.match([&](const MTPDchatInviteExported &data) {
|
||||
return Link{
|
||||
.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,
|
||||
.startDate = data.vstart_date().value_or_empty(),
|
||||
.expireDate = data.vexpire_date().value_or_empty(),
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ Key ExtractKey(const QString &query) {
|
|||
const auto params = parse();
|
||||
const auto channel = params.value("channel");
|
||||
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)) {
|
||||
const auto params = parse();
|
||||
const auto domain = params.value("domain");
|
||||
|
|
@ -112,7 +112,7 @@ std::optional<HistoryItem*> SingleMessageSearch::performLookupByChannel(
|
|||
&& received.messageIds.front() == postId) {
|
||||
_cache.emplace(
|
||||
_requestKey,
|
||||
FullMsgId(channel->bareId(), postId));
|
||||
FullMsgId(peerToChannel(channel->id), postId));
|
||||
ready();
|
||||
} else {
|
||||
fail();
|
||||
|
|
@ -142,7 +142,7 @@ std::optional<HistoryItem*> SingleMessageSearch::performLookupById(
|
|||
_requestId = _session->api().request(MTPchannels_GetChannels(
|
||||
MTP_vector<MTPInputChannel>(
|
||||
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) {
|
||||
result.match([&](const auto &data) {
|
||||
const auto peer = _session->data().processChats(data.vchats());
|
||||
|
|
@ -212,7 +212,7 @@ std::optional<HistoryItem*> SingleMessageSearch::performLookup(
|
|||
if (!_requestKey.domainOrId[0].isDigit()) {
|
||||
return performLookupByUsername(_requestKey.domainOrId, ready);
|
||||
}
|
||||
const auto channelId = _requestKey.domainOrId.toInt();
|
||||
const auto channelId = ChannelId(_requestKey.domainOrId.toULongLong());
|
||||
return performLookupById(channelId, ready);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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_messageEntityMentionName: {
|
||||
const auto &d = entity.c_messageEntityMentionName();
|
||||
const auto userId = UserId(d.vuser_id());
|
||||
const auto data = [&] {
|
||||
if (session) {
|
||||
if (const auto user = session->data().userLoaded(d.vuser_id().v)) {
|
||||
if (const auto user = session->data().userLoaded(userId)) {
|
||||
return MentionNameDataFromFields({
|
||||
d.vuser_id().v,
|
||||
user->accessHash() });
|
||||
userId.bare,
|
||||
user->accessHash()
|
||||
});
|
||||
}
|
||||
}
|
||||
return MentionNameDataFromFields(d.vuser_id().v);
|
||||
return MentionNameDataFromFields(userId.bare);
|
||||
}();
|
||||
result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data });
|
||||
} break;
|
||||
|
|
@ -53,10 +55,11 @@ EntitiesInText EntitiesFromMTP(
|
|||
const auto &d = entity.c_inputMessageEntityMentionName();
|
||||
const auto data = [&] {
|
||||
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) {
|
||||
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();
|
||||
}();
|
||||
|
|
@ -130,7 +133,7 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
|
|||
if (uid == session->userId()) {
|
||||
return MTP_inputUserSelf();
|
||||
} 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();
|
||||
|
|
@ -148,7 +151,7 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
|
|||
case EntityType::MentionName: {
|
||||
auto inputUser = [&](const QString &data) -> MTPInputUser {
|
||||
auto fields = MentionNameDataToFields(data);
|
||||
if (session && fields.userId == session->userId()) {
|
||||
if (session && fields.userId == session->userId().bare) {
|
||||
return MTP_inputUserSelf();
|
||||
} else if (fields.userId) {
|
||||
return MTP_inputUser(MTP_int(fields.userId), MTP_long(fields.accessHash));
|
||||
|
|
|
|||
|
|
@ -131,13 +131,13 @@ bool MentionUsersLoaded(
|
|||
for (const auto &entity : entities.v) {
|
||||
auto type = entity.type();
|
||||
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;
|
||||
}
|
||||
} else if (type == mtpc_inputMessageEntityMentionName) {
|
||||
auto &inputUser = entity.c_inputMessageEntityMentionName().vuser_id();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -157,7 +157,7 @@ DataIsLoadedResult AllDataLoadedForMessage(
|
|||
}
|
||||
}
|
||||
if (const auto viaBotId = message.vvia_bot_id()) {
|
||||
if (!session->data().userLoaded(viaBotId->v)) {
|
||||
if (!session->data().userLoaded(*viaBotId)) {
|
||||
return DataIsLoadedResult::NotLoaded;
|
||||
}
|
||||
}
|
||||
|
|
@ -181,19 +181,19 @@ DataIsLoadedResult AllDataLoadedForMessage(
|
|||
}
|
||||
return message.vaction().match(
|
||||
[&](const MTPDmessageActionChatAddUser &action) {
|
||||
for (const MTPint &userId : action.vusers().v) {
|
||||
if (!session->data().userLoaded(userId.v)) {
|
||||
for (const auto &userId : action.vusers().v) {
|
||||
if (!session->data().userLoaded(userId)) {
|
||||
return DataIsLoadedResult::NotLoaded;
|
||||
}
|
||||
}
|
||||
return DataIsLoadedResult::Ok;
|
||||
}, [&](const MTPDmessageActionChatJoinedByLink &action) {
|
||||
if (!session->data().userLoaded(action.vinviter_id().v)) {
|
||||
if (!session->data().userLoaded(action.vinviter_id())) {
|
||||
return DataIsLoadedResult::NotLoaded;
|
||||
}
|
||||
return DataIsLoadedResult::Ok;
|
||||
}, [&](const MTPDmessageActionChatDeleteUser &action) {
|
||||
if (!session->data().userLoaded(action.vuser_id().v)) {
|
||||
if (!session->data().userLoaded(action.vuser_id())) {
|
||||
return DataIsLoadedResult::NotLoaded;
|
||||
}
|
||||
return DataIsLoadedResult::Ok;
|
||||
|
|
@ -1020,7 +1020,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
|||
| MTPDmessage::Flag::f_from_id;
|
||||
const auto peerUserId = d.is_out()
|
||||
? d.vuser_id()
|
||||
: MTP_int(_session->userId());
|
||||
: MTP_int(_session->userId().bare); // #TODO ids
|
||||
_session->data().addNewMessage(
|
||||
MTP_message(
|
||||
MTP_flags(flags),
|
||||
|
|
@ -1309,8 +1309,8 @@ void Updates::applyUpdates(
|
|||
const auto viaBotId = d.vvia_bot_id();
|
||||
const auto entities = d.ventities();
|
||||
const auto fwd = d.vfwd_from();
|
||||
if (!session().data().userLoaded(d.vuser_id().v)
|
||||
|| (viaBotId && !session().data().userLoaded(viaBotId->v))
|
||||
if (!session().data().userLoaded(d.vuser_id())
|
||||
|| (viaBotId && !session().data().userLoaded(*viaBotId))
|
||||
|| (entities && !MentionUsersLoaded(&session(), *entities))
|
||||
|| (fwd && !ForwardedInfoDataLoaded(&session(), *fwd))) {
|
||||
MTP_LOG(0, ("getDifference "
|
||||
|
|
@ -1326,14 +1326,14 @@ void Updates::applyUpdates(
|
|||
|
||||
case mtpc_updateShortChatMessage: {
|
||||
auto &d = updates.c_updateShortChatMessage();
|
||||
const auto noFrom = !session().data().userLoaded(d.vfrom_id().v);
|
||||
const auto chat = session().data().chatLoaded(d.vchat_id().v);
|
||||
const auto noFrom = !session().data().userLoaded(d.vfrom_id());
|
||||
const auto chat = session().data().chatLoaded(d.vchat_id());
|
||||
const auto viaBotId = d.vvia_bot_id();
|
||||
const auto entities = d.ventities();
|
||||
const auto fwd = d.vfwd_from();
|
||||
if (!chat
|
||||
|| noFrom
|
||||
|| (viaBotId && !session().data().userLoaded(viaBotId->v))
|
||||
|| (viaBotId && !session().data().userLoaded(*viaBotId))
|
||||
|| (entities && !MentionUsersLoaded(&session(), *entities))
|
||||
|| (fwd && !ForwardedInfoDataLoaded(&session(), *fwd))) {
|
||||
MTP_LOG(0, ("getDifference "
|
||||
|
|
@ -1491,7 +1491,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
|
||||
case mtpc_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 (!_byMinChannelTimer.isActive()) {
|
||||
// getDifference after timeout.
|
||||
|
|
@ -1537,7 +1537,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
|
||||
case mtpc_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->ptsRequesting()) { // skip global updates while getting channel difference
|
||||
|
|
@ -1623,7 +1623,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
|
||||
case mtpc_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->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().sendWebPageGamePollNotifications();
|
||||
|
||||
auto channel = session().data().channelLoaded(d.vchannel_id().v);
|
||||
auto channel = session().data().channelLoaded(d.vchannel_id());
|
||||
if (channel && !_handlingChannelDifference) {
|
||||
if (channel->ptsRequesting()) { // skip global updates while getting channel difference
|
||||
return;
|
||||
|
|
@ -1683,27 +1683,25 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
handleSendActionUpdate(
|
||||
peerFromUser(d.vuser_id()),
|
||||
0,
|
||||
d.vuser_id().v,
|
||||
peerFromUser(d.vuser_id()),
|
||||
d.vaction());
|
||||
} break;
|
||||
|
||||
case mtpc_updateChatUserTyping: {
|
||||
auto &d = update.c_updateChatUserTyping();
|
||||
const auto fromId = peerFromMTP(d.vfrom_id());
|
||||
handleSendActionUpdate(
|
||||
peerFromChat(d.vchat_id()),
|
||||
0,
|
||||
fromId,
|
||||
peerFromMTP(d.vfrom_id()),
|
||||
d.vaction());
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelUserTyping: {
|
||||
const auto &d = update.c_updateChannelUserTyping();
|
||||
const auto fromId = peerFromMTP(d.vfrom_id());
|
||||
handleSendActionUpdate(
|
||||
peerFromChannel(d.vchannel_id()),
|
||||
d.vtop_msg_id().value_or_empty(),
|
||||
fromId,
|
||||
peerFromMTP(d.vfrom_id()),
|
||||
d.vaction());
|
||||
} break;
|
||||
|
||||
|
|
@ -1729,7 +1727,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
|
||||
case mtpc_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()) {
|
||||
case mtpc_userStatusEmpty: user->onlineTill = 0; break;
|
||||
case mtpc_userStatusRecently:
|
||||
|
|
@ -1746,7 +1744,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
user,
|
||||
Data::PeerUpdate::Flag::OnlineStatus);
|
||||
}
|
||||
if (d.vuser_id().v == session().userId()) {
|
||||
if (UserId(d.vuser_id()) == session().userId()) {
|
||||
if (d.vstatus().type() == mtpc_userStatusOffline
|
||||
|| d.vstatus().type() == mtpc_userStatusEmpty) {
|
||||
updateOnline(true);
|
||||
|
|
@ -1763,7 +1761,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
|
||||
case mtpc_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()) {
|
||||
user->setName(
|
||||
TextUtilities::SingleLine(qs(d.vfirst_name())),
|
||||
|
|
@ -1782,7 +1780,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
|
||||
case mtpc_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->loadUserpic();
|
||||
// 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()) {
|
||||
session().storage().remove(Storage::UserPhotosRemoveAfter(
|
||||
user->bareId(),
|
||||
peerToUser(user->id),
|
||||
user->userpicPhotoId()));
|
||||
//} else {
|
||||
// session().storage().add(Storage::UserPhotosAddNew(
|
||||
// user->bareId(),
|
||||
// peerToUser(user->id),
|
||||
// user->userpicPhotoId()));
|
||||
//}
|
||||
}
|
||||
|
|
@ -1831,7 +1829,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
|
||||
case mtpc_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());
|
||||
if (newPhone != user->phone()) {
|
||||
user->setPhone(newPhone);
|
||||
|
|
@ -1914,8 +1912,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
auto &d = update.c_updatePrivacy();
|
||||
const auto allChatsLoaded = [&](const MTPVector<MTPint> &ids) {
|
||||
for (const auto &chatId : ids.v) {
|
||||
if (!session().data().chatLoaded(chatId.v)
|
||||
&& !session().data().channelLoaded(chatId.v)) {
|
||||
if (!session().data().chatLoaded(chatId)
|
||||
&& !session().data().channelLoaded(chatId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1999,7 +1997,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
}
|
||||
DEBUG_LOG(("API Error: "
|
||||
"pinned chat not loaded for peer %1, folder: %2"
|
||||
).arg(id
|
||||
).arg(id.value
|
||||
).arg(folderId
|
||||
));
|
||||
return false;
|
||||
|
|
@ -2027,7 +2025,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
|
||||
case mtpc_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);
|
||||
if (channel->amIn()) {
|
||||
if (channel->isMegagroup()
|
||||
|
|
@ -2049,7 +2047,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
|
||||
case mtpc_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();
|
||||
if (!pts || channel->pts() < pts->v) {
|
||||
getChannelDifference(channel);
|
||||
|
|
@ -2108,7 +2106,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
|
||||
case mtpc_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);
|
||||
if (const auto history = session().data().historyLoaded(channel)) {
|
||||
history->clearUpTill(d.vavailable_min_id().v);
|
||||
|
|
|
|||
|
|
@ -692,7 +692,7 @@ QString ApiWrap::exportDirectMessageLink(
|
|||
if (inRepliesContext) {
|
||||
if (const auto rootId = item->replyToTop()) {
|
||||
const auto root = item->history()->owner().message(
|
||||
channel->bareId(),
|
||||
peerToChannel(channel->id),
|
||||
rootId);
|
||||
const auto sender = root
|
||||
? root->discussionPostOriginalSender()
|
||||
|
|
@ -715,7 +715,7 @@ QString ApiWrap::exportDirectMessageLink(
|
|||
}
|
||||
const auto base = linkChannel->hasUsername()
|
||||
? linkChannel->username
|
||||
: "c/" + QString::number(linkChannel->bareId());
|
||||
: "c/" + QString::number(peerToChannel(linkChannel->id).bare);
|
||||
const auto query = base
|
||||
+ '/'
|
||||
+ QString::number(linkItemId)
|
||||
|
|
@ -776,7 +776,7 @@ void ApiWrap::requestContacts() {
|
|||
for (const auto &contact : d.vcontacts().v) {
|
||||
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()) {
|
||||
_session->user()->setIsContact(true);
|
||||
}
|
||||
|
|
@ -2267,7 +2267,7 @@ void ApiWrap::updatePrivacyLastSeens(const QVector<MTPPrivacyRule> &rules) {
|
|||
for (const auto &item : result.v) {
|
||||
Assert(item.type() == mtpc_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 newOnlineTill = OnlineTillFromStatus(data.vstatus(), oldOnlineTill);
|
||||
if (oldOnlineTill != newOnlineTill) {
|
||||
|
|
@ -3566,7 +3566,7 @@ void ApiWrap::userPhotosDone(
|
|||
}
|
||||
}
|
||||
_session->storage().add(Storage::UserPhotosAddSlice(
|
||||
user->id,
|
||||
peerToUser(user->id),
|
||||
std::move(photoIds),
|
||||
fullCount
|
||||
));
|
||||
|
|
@ -4297,7 +4297,7 @@ void ApiWrap::sendSharedContact(
|
|||
MTP_string(firstName),
|
||||
MTP_string(lastName),
|
||||
MTP_string(vcard),
|
||||
MTP_int(userId)),
|
||||
MTP_int(userId.bare)), // #TODO ids
|
||||
MTPReplyMarkup(),
|
||||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(views),
|
||||
|
|
@ -5114,7 +5114,7 @@ void ApiWrap::clearPeerPhoto(not_null<PhotoData*> photo) {
|
|||
MTP_vector<MTPInputPhoto>(1, photo->mtpInput())
|
||||
)).send();
|
||||
_session->storage().remove(Storage::UserPhotosRemoveOne(
|
||||
self->bareId(),
|
||||
peerToUser(self->id),
|
||||
photo->id));
|
||||
}
|
||||
}
|
||||
|
|
@ -5291,11 +5291,11 @@ auto ApiWrap::parsePrivacy(const QVector<MTPPrivacyRule> &rules)
|
|||
}, [&](const MTPDprivacyValueAllowChatParticipants &data) {
|
||||
const auto &chats = data.vchats().v;
|
||||
always.reserve(always.size() + chats.size());
|
||||
for (const auto chatId : chats) {
|
||||
const auto chat = _session->data().chatLoaded(chatId.v);
|
||||
for (const auto &chatId : chats) {
|
||||
const auto chat = _session->data().chatLoaded(chatId);
|
||||
const auto peer = chat
|
||||
? static_cast<PeerData*>(chat)
|
||||
: _session->data().channelLoaded(chatId.v);
|
||||
: _session->data().channelLoaded(chatId);
|
||||
if (peer
|
||||
&& !base::contains(never, peer)
|
||||
&& !base::contains(always, peer)) {
|
||||
|
|
@ -5319,11 +5319,11 @@ auto ApiWrap::parsePrivacy(const QVector<MTPPrivacyRule> &rules)
|
|||
}, [&](const MTPDprivacyValueDisallowChatParticipants &data) {
|
||||
const auto &chats = data.vchats().v;
|
||||
never.reserve(never.size() + chats.size());
|
||||
for (const auto chatId : chats) {
|
||||
const auto chat = _session->data().chatLoaded(chatId.v);
|
||||
for (const auto &chatId : chats) {
|
||||
const auto chat = _session->data().chatLoaded(chatId);
|
||||
const auto peer = chat
|
||||
? static_cast<PeerData*>(chat)
|
||||
: _session->data().channelLoaded(chatId.v);
|
||||
: _session->data().channelLoaded(chatId);
|
||||
if (peer
|
||||
&& !base::contains(always, peer)
|
||||
&& !base::contains(never, peer)) {
|
||||
|
|
|
|||
|
|
@ -76,6 +76,15 @@ inline QString ToString(uint64 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
|
||||
|
||||
template <
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ void ChatCreateDone(
|
|||
}
|
||||
| [&](auto chats) {
|
||||
return navigation->session().data().chat(
|
||||
chats->front().c_chat().vid().v);
|
||||
chats->front().c_chat().vid());
|
||||
}
|
||||
| [&](not_null<ChatData*> chat) {
|
||||
if (!image.isNull()) {
|
||||
|
|
@ -268,7 +268,7 @@ AddContactBox::AddContactBox(
|
|||
this,
|
||||
st::defaultInputField,
|
||||
tr::lng_contact_phone(),
|
||||
ExtractPhonePrefix(session->user()->phone()),
|
||||
Ui::ExtractPhonePrefix(session->user()->phone()),
|
||||
phone)
|
||||
, _invertOrder(langFirstNameGoesSecond()) {
|
||||
if (!phone.isEmpty()) {
|
||||
|
|
@ -401,7 +401,7 @@ void AddContactBox::save() {
|
|||
const auto extractUser = [&](const MTPImportedContact &data) {
|
||||
return data.match([&](const MTPDimportedContact &data) {
|
||||
return (data.vclient_id().v == _contactId)
|
||||
? _session->data().userLoaded(data.vuser_id().v)
|
||||
? _session->data().userLoaded(data.vuser_id())
|
||||
: nullptr;
|
||||
});
|
||||
};
|
||||
|
|
@ -687,7 +687,7 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
|
|||
}
|
||||
| [&](auto chats) {
|
||||
return _navigation->session().data().channel(
|
||||
chats->front().c_channel().vid().v);
|
||||
chats->front().c_channel().vid());
|
||||
}
|
||||
| [&](not_null<ChannelData*> channel) {
|
||||
auto image = _photo->takeResultImage();
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ AdminLog::OwnedItem GenerateTextItem(
|
|||
| (out ? Flag::f_out : Flag(0));
|
||||
const auto clientFlags = MTPDmessage_ClientFlag::f_fake_history_item;
|
||||
const auto replyTo = 0;
|
||||
const auto viaBotId = 0;
|
||||
const auto viaBotId = UserId(0);
|
||||
const auto item = history->makeMessage(
|
||||
++id,
|
||||
flags,
|
||||
|
|
@ -402,14 +402,12 @@ BackgroundPreviewBox::BackgroundPreviewBox(
|
|||
, _controller(controller)
|
||||
, _text1(GenerateTextItem(
|
||||
delegate(),
|
||||
_controller->session().data().history(
|
||||
peerFromUser(PeerData::kServiceNotificationsId)),
|
||||
_controller->session().data().history(PeerData::kServiceNotificationsId),
|
||||
tr::lng_background_text1(tr::now),
|
||||
false))
|
||||
, _text2(GenerateTextItem(
|
||||
delegate(),
|
||||
_controller->session().data().history(
|
||||
peerFromUser(PeerData::kServiceNotificationsId)),
|
||||
_controller->session().data().history(PeerData::kServiceNotificationsId),
|
||||
tr::lng_background_text2(tr::now),
|
||||
true))
|
||||
, _paper(paper)
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ void ChangePhoneBox::EnterPhone::prepare() {
|
|||
this,
|
||||
st::defaultInputField,
|
||||
tr::lng_change_phone_new_title(),
|
||||
ExtractPhonePrefix(_session->user()->phone()),
|
||||
Ui::ExtractPhonePrefix(_session->user()->phone()),
|
||||
phoneValue);
|
||||
|
||||
_phone->resize(st::boxWidth - 2 * st::boxPadding.left(), _phone->height());
|
||||
|
|
|
|||
|
|
@ -304,9 +304,10 @@ void ConfirmBox::mouseReleaseEvent(QMouseEvent *e) {
|
|||
_lastMousePos = e->globalPos();
|
||||
updateHover();
|
||||
if (const auto activated = ClickHandler::unpressed()) {
|
||||
const auto guard = window();
|
||||
Ui::hideLayer();
|
||||
ActivateClickHandler(guard, activated, e->button());
|
||||
ActivateClickHandler(window(), activated, e->button());
|
||||
crl::on_main(this, [=] {
|
||||
closeBox();
|
||||
});
|
||||
return;
|
||||
}
|
||||
BoxContent::mouseReleaseEvent(e);
|
||||
|
|
|
|||
|
|
@ -71,14 +71,6 @@ void ShowPhoneBannedError(const QString &phone) {
|
|||
[=] { 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(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ class Session;
|
|||
} // namespace Main
|
||||
|
||||
void ShowPhoneBannedError(const QString &phone);
|
||||
[[nodiscard]] QString ExtractPhonePrefix(const QString &phone);
|
||||
|
||||
class SentCodeField : public Ui::InputField {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -182,11 +182,11 @@ QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() {
|
|||
return result;
|
||||
};
|
||||
const auto collectInputChats = [](const auto &peers) {
|
||||
auto result = QVector<MTPint>();
|
||||
auto result = QVector<MTPint>(); // #TODO ids
|
||||
result.reserve(peers.size());
|
||||
for (const auto peer : peers) {
|
||||
if (!peer->isUser()) {
|
||||
result.push_back(MTP_int(peer->bareId()));
|
||||
result.push_back(peerToBareMTPInt(peer->id));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ private:
|
|||
}
|
||||
|
||||
[[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)) {
|
||||
|
|
|
|||
|
|
@ -361,7 +361,11 @@ void PasscodeBox::closeReplacedBy() {
|
|||
}
|
||||
|
||||
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();
|
||||
_setRequest = 0;
|
||||
|
||||
|
|
@ -378,20 +382,19 @@ void PasscodeBox::setPasswordFail(const MTP::Error &error) {
|
|||
|
||||
closeReplacedBy();
|
||||
_setRequest = 0;
|
||||
const auto &err = error.type();
|
||||
if (err == qstr("PASSWORD_HASH_INVALID")
|
||||
|| err == qstr("SRP_PASSWORD_CHANGED")) {
|
||||
if (type == qstr("PASSWORD_HASH_INVALID")
|
||||
|| type == qstr("SRP_PASSWORD_CHANGED")) {
|
||||
if (_oldPasscode->isHidden()) {
|
||||
_passwordReloadNeeded.fire({});
|
||||
closeBox();
|
||||
} else {
|
||||
badOldPasscode();
|
||||
}
|
||||
} else if (err == qstr("SRP_ID_INVALID")) {
|
||||
} else if (type == qstr("SRP_ID_INVALID")) {
|
||||
handleSrpIdInvalid();
|
||||
//} else if (err == qstr("NEW_PASSWORD_BAD")) {
|
||||
//} else if (err == qstr("NEW_SALT_INVALID")) {
|
||||
} else if (err == qstr("EMAIL_INVALID")) {
|
||||
//} else if (type == qstr("NEW_PASSWORD_BAD")) {
|
||||
//} else if (type == qstr("NEW_SALT_INVALID")) {
|
||||
} else if (type == qstr("EMAIL_INVALID")) {
|
||||
_emailError = tr::lng_cloud_password_bad_email(tr::now);
|
||||
_recoverEmail->setFocus();
|
||||
_recoverEmail->showError();
|
||||
|
|
@ -682,12 +685,15 @@ void PasscodeBox::serverError() {
|
|||
}
|
||||
|
||||
bool PasscodeBox::handleCustomCheckError(const MTP::Error &error) {
|
||||
const auto &type = error.type();
|
||||
if (MTP::IsFloodError(error)
|
||||
return handleCustomCheckError(error.type());
|
||||
}
|
||||
|
||||
bool PasscodeBox::handleCustomCheckError(const QString &type) {
|
||||
if (MTP::IsFloodError(type)
|
||||
|| type == qstr("PASSWORD_HASH_INVALID")
|
||||
|| type == qstr("SRP_PASSWORD_CHANGED")
|
||||
|| type == qstr("SRP_ID_INVALID")) {
|
||||
setPasswordFail(error);
|
||||
setPasswordFail(type);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ public:
|
|||
rpl::producer<> clearUnconfirmedPassword() const;
|
||||
|
||||
bool handleCustomCheckError(const MTP::Error &error);
|
||||
bool handleCustomCheckError(const QString &type);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
|
@ -82,6 +83,7 @@ private:
|
|||
|
||||
void setPasswordDone(const QByteArray &newPasswordBytes);
|
||||
void setPasswordFail(const MTP::Error &error);
|
||||
void setPasswordFail(const QString &type);
|
||||
void setPasswordFail(
|
||||
const QByteArray &newPasswordBytes,
|
||||
const QString &email,
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@ void PeerListBox::createMultiSelect() {
|
|||
if (_controller->handleDeselectForeignRow(itemId)) {
|
||||
return;
|
||||
}
|
||||
if (const auto peer = _controller->session().data().peerLoaded(itemId)) {
|
||||
if (const auto row = peerListFindRow(peer->id)) {
|
||||
if (const auto peer = _controller->session().data().peerLoaded(PeerId(itemId))) {
|
||||
if (const auto row = peerListFindRow(itemId)) {
|
||||
content()->changeCheckState(row, false, anim::type::normal);
|
||||
update();
|
||||
}
|
||||
|
|
@ -278,11 +278,11 @@ void PeerListController::search(const QString &query) {
|
|||
}
|
||||
|
||||
void PeerListController::peerListSearchAddRow(not_null<PeerData*> peer) {
|
||||
if (auto row = delegate()->peerListFindRow(peer->id)) {
|
||||
Assert(row->id() == row->peer()->id);
|
||||
if (auto row = delegate()->peerListFindRow(peer->id.value)) {
|
||||
Assert(row->id() == row->peer()->id.value);
|
||||
delegate()->peerListAppendFoundRow(row);
|
||||
} else if (auto row = createSearchRow(peer)) {
|
||||
Assert(row->id() == row->peer()->id);
|
||||
Assert(row->id() == row->peer()->id.value);
|
||||
delegate()->peerListAppendSearchRow(std::move(row));
|
||||
}
|
||||
}
|
||||
|
|
@ -356,7 +356,7 @@ void PeerListBox::addSelectItem(
|
|||
? tr::lng_replies_messages(tr::now)
|
||||
: peer->shortName();
|
||||
addSelectItem(
|
||||
peer->id,
|
||||
peer->id.value,
|
||||
text,
|
||||
PaintUserpicCallback(peer, respect),
|
||||
animated);
|
||||
|
|
@ -423,7 +423,7 @@ auto PeerListBox::collectSelectedRows()
|
|||
result.reserve(items.size());
|
||||
for (const auto itemId : items) {
|
||||
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(peer, peer->id) {
|
||||
: PeerListRow(peer, peer->id.value) {
|
||||
}
|
||||
|
||||
PeerListRow::PeerListRow(not_null<PeerData*> peer, PeerListRowId id)
|
||||
|
|
@ -914,7 +914,7 @@ void PeerListContent::addRowEntry(not_null<PeerListRow*> row) {
|
|||
addToSearchIndex(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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1772,7 +1772,7 @@ void PeerListContent::restoreState(
|
|||
auto searchWords = TextUtilities::PrepareSearchWords(query);
|
||||
setSearchQuery(query, searchWords.join(' '));
|
||||
for (auto peer : state->filterResults) {
|
||||
if (auto existingRow = findRow(peer->id)) {
|
||||
if (auto existingRow = findRow(peer->id.value)) {
|
||||
_filterResults.push_back(existingRow);
|
||||
} else if (auto row = _controller->createSearchRow(peer)) {
|
||||
appendSearchRow(std::move(row));
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@ std::unique_ptr<PeerListRow> ChatsListBoxController::createSearchRow(not_null<Pe
|
|||
}
|
||||
|
||||
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));
|
||||
return false;
|
||||
}
|
||||
|
|
@ -451,7 +451,7 @@ void ContactsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
|||
}
|
||||
|
||||
bool ContactsBoxController::appendRow(not_null<UserData*> user) {
|
||||
if (auto row = delegate()->peerListFindRow(user->id)) {
|
||||
if (auto row = delegate()->peerListFindRow(user->id.value)) {
|
||||
updateRowHook(row);
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ auto PeerListsBox::collectSelectedRows()
|
|||
return false;
|
||||
}();
|
||||
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();
|
||||
if (const auto peer = session->data().peerLoaded(itemId)) {
|
||||
if (const auto peer = session->data().peerLoaded(PeerId(itemId))) {
|
||||
const auto id = peer->id;
|
||||
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(
|
||||
row,
|
||||
false,
|
||||
|
|
@ -385,7 +385,7 @@ void PeerListsBox::addSelectItem(
|
|||
not_null<PeerData*> peer,
|
||||
anim::type animated) {
|
||||
addSelectItem(
|
||||
peer->id,
|
||||
peer->id.value,
|
||||
peer->shortName(),
|
||||
PaintUserpicCallback(peer, false),
|
||||
animated);
|
||||
|
|
|
|||
|
|
@ -661,12 +661,12 @@ void AddSpecialBoxController::editAdminDone(
|
|||
using Flag = MTPDchannelParticipantCreator::Flag;
|
||||
_additional.applyParticipant(MTP_channelParticipantCreator(
|
||||
MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank),
|
||||
MTP_int(user->bareId()),
|
||||
peerToBareMTPInt(user->id),
|
||||
rights,
|
||||
MTP_string(rank)));
|
||||
} else if (rights.c_chatAdminRights().vflags().v == 0) {
|
||||
_additional.applyParticipant(MTP_channelParticipant(
|
||||
MTP_int(user->bareId()),
|
||||
peerToBareMTPInt(user->id),
|
||||
MTP_int(date)));
|
||||
} else {
|
||||
using Flag = MTPDchannelParticipantAdmin::Flag;
|
||||
|
|
@ -674,11 +674,11 @@ void AddSpecialBoxController::editAdminDone(
|
|||
_additional.applyParticipant(MTP_channelParticipantAdmin(
|
||||
MTP_flags(Flag::f_can_edit
|
||||
| (rank.isEmpty() ? Flag(0) : Flag::f_rank)),
|
||||
MTP_int(user->bareId()),
|
||||
peerToBareMTPInt(user->id),
|
||||
MTPint(), // inviter_id
|
||||
MTP_int(alreadyPromotedBy
|
||||
? alreadyPromotedBy->bareId()
|
||||
: user->session().userId()),
|
||||
peerToBareMTPInt(alreadyPromotedBy
|
||||
? alreadyPromotedBy->id
|
||||
: user->session().userPeerId()),
|
||||
MTP_int(date),
|
||||
rights,
|
||||
MTP_string(rank)));
|
||||
|
|
@ -763,7 +763,7 @@ void AddSpecialBoxController::editRestrictedDone(
|
|||
if (Data::ChatBannedRightsFlags(rights) == 0) {
|
||||
if (const auto user = participant->asUser()) {
|
||||
_additional.applyParticipant(MTP_channelParticipant(
|
||||
MTP_int(user->bareId()),
|
||||
peerToBareMTPInt(user->id),
|
||||
MTP_int(date)));
|
||||
} else {
|
||||
_additional.setExternal(participant);
|
||||
|
|
@ -777,14 +777,10 @@ void AddSpecialBoxController::editRestrictedDone(
|
|||
MTP_flags(kicked
|
||||
? MTPDchannelParticipantBanned::Flag::f_left
|
||||
: MTPDchannelParticipantBanned::Flag(0)),
|
||||
(participant->isUser()
|
||||
? MTP_peerUser(MTP_int(participant->bareId()))
|
||||
: participant->isChat()
|
||||
? MTP_peerChat(MTP_int(participant->bareId()))
|
||||
: MTP_peerChannel(MTP_int(participant->bareId()))),
|
||||
MTP_int(alreadyRestrictedBy
|
||||
? alreadyRestrictedBy->bareId()
|
||||
: participant->session().userId()),
|
||||
peerToMTP(participant->id),
|
||||
peerToBareMTPInt(alreadyRestrictedBy
|
||||
? alreadyRestrictedBy->id
|
||||
: participant->session().userPeerId()),
|
||||
MTP_int(date),
|
||||
rights));
|
||||
}
|
||||
|
|
@ -861,7 +857,7 @@ void AddSpecialBoxController::kickUser(
|
|||
}
|
||||
|
||||
bool AddSpecialBoxController::appendRow(not_null<PeerData*> participant) {
|
||||
if (delegate()->peerListFindRow(participant->id)
|
||||
if (delegate()->peerListFindRow(participant->id.value)
|
||||
|| (_excludeSelf && participant->isSelf())) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -870,7 +866,7 @@ bool AddSpecialBoxController::appendRow(not_null<PeerData*> participant) {
|
|||
}
|
||||
|
||||
bool AddSpecialBoxController::prependRow(not_null<UserData*> user) {
|
||||
if (delegate()->peerListFindRow(user->id)) {
|
||||
if (delegate()->peerListFindRow(user->id.value)) {
|
||||
return false;
|
||||
}
|
||||
delegate()->peerListPrependRow(createRow(user));
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ constexpr auto kMaxUserFirstLastName = 64; // See also add_contact_box.
|
|||
QString UserPhone(not_null<UserData*> user) {
|
||||
const auto phone = user->phone();
|
||||
return phone.isEmpty()
|
||||
? user->owner().findContactPhone(user->bareId())
|
||||
? user->owner().findContactPhone(peerToUser(user->id))
|
||||
: phone;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ int Controller::contentWidth() const {
|
|||
|
||||
void Controller::prepare() {
|
||||
const auto appendRow = [&](not_null<PeerData*> chat) {
|
||||
if (delegate()->peerListFindRow(chat->id)) {
|
||||
if (delegate()->peerListFindRow(chat->id.value)) {
|
||||
return;
|
||||
}
|
||||
auto row = std::make_unique<PeerListRow>(chat);
|
||||
|
|
|
|||
|
|
@ -609,7 +609,7 @@ UserData *ParticipantsAdditionalData::applyCreator(
|
|||
|
||||
UserData *ParticipantsAdditionalData::applyAdmin(
|
||||
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) {
|
||||
return nullptr;
|
||||
} else if (const auto chat = _peer->asChat()) {
|
||||
|
|
@ -633,7 +633,7 @@ UserData *ParticipantsAdditionalData::applyAdmin(
|
|||
} else {
|
||||
_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);
|
||||
if (i == _adminPromotedBy.end()) {
|
||||
_adminPromotedBy.emplace(user, by);
|
||||
|
|
@ -648,7 +648,7 @@ UserData *ParticipantsAdditionalData::applyAdmin(
|
|||
}
|
||||
|
||||
UserData *ParticipantsAdditionalData::applyRegular(MTPint userId) {
|
||||
const auto user = _peer->owner().userLoaded(userId.v);
|
||||
const auto user = _peer->owner().userLoaded(userId);
|
||||
if (!user) {
|
||||
return nullptr;
|
||||
} else if (const auto chat = _peer->asChat()) {
|
||||
|
|
@ -687,7 +687,7 @@ PeerData *ParticipantsAdditionalData::applyBanned(
|
|||
_kicked.erase(participant);
|
||||
}
|
||||
_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);
|
||||
if (i == _restrictedBy.end()) {
|
||||
_restrictedBy.emplace(participant, by);
|
||||
|
|
@ -727,7 +727,7 @@ ParticipantsOnlineSorter::ParticipantsOnlineSorter(
|
|||
Data::PeerUpdate::Flag::OnlineStatus
|
||||
) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
|
||||
const auto peerId = update.peer->id;
|
||||
if (const auto row = _delegate->peerListFindRow(peerId)) {
|
||||
if (const auto row = _delegate->peerListFindRow(peerId.value)) {
|
||||
row->refreshStatus();
|
||||
sortDelayed();
|
||||
}
|
||||
|
|
@ -829,7 +829,7 @@ void ParticipantsBoxController::setupListChangeViewers() {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (const auto row = delegate()->peerListFindRow(user->id)) {
|
||||
if (const auto row = delegate()->peerListFindRow(user->id.value)) {
|
||||
delegate()->peerListPartitionRows([&](const PeerListRow &row) {
|
||||
return (row.peer() == user);
|
||||
});
|
||||
|
|
@ -845,7 +845,7 @@ void ParticipantsBoxController::setupListChangeViewers() {
|
|||
channel->owner().megagroupParticipantRemoved(
|
||||
channel
|
||||
) | 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()->peerListRefreshRows();
|
||||
|
|
@ -1459,8 +1459,6 @@ void ParticipantsBoxController::rowActionClicked(
|
|||
base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
|
||||
QWidget *parent,
|
||||
not_null<PeerListRow*> row) {
|
||||
Expects(row->peer()->isUser());
|
||||
|
||||
const auto chat = _peer->asChat();
|
||||
const auto channel = _peer->asChannel();
|
||||
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);
|
||||
if (_navigation) {
|
||||
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, [=] {
|
||||
_navigation->showPeerInfo(participant); }));
|
||||
}
|
||||
|
|
@ -1509,12 +1511,12 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
|
|||
: tr::lng_context_promote_admin)(tr::now),
|
||||
crl::guard(this, [=] { showAdmin(user); }));
|
||||
}
|
||||
if (_additional.canRestrictParticipant(participant)) {
|
||||
if (user && _additional.canRestrictParticipant(participant)) {
|
||||
const auto canRestrictWithoutKick = [&] {
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
return chat->amCreator();
|
||||
}
|
||||
return _peer->isMegagroup();
|
||||
return _peer->isMegagroup() && !_peer->isGigagroup();
|
||||
}();
|
||||
if (canRestrictWithoutKick) {
|
||||
result->addAction(
|
||||
|
|
@ -1522,7 +1524,7 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
|
|||
crl::guard(this, [=] { showRestricted(user); }));
|
||||
}
|
||||
}
|
||||
if (_additional.canRemoveParticipant(participant)) {
|
||||
if (user && _additional.canRemoveParticipant(participant)) {
|
||||
if (!_additional.isKicked(participant)) {
|
||||
const auto isGroup = _peer->isChat() || _peer->isMegagroup();
|
||||
result->addAction(
|
||||
|
|
@ -1591,12 +1593,12 @@ void ParticipantsBoxController::editAdminDone(
|
|||
using Flag = MTPDchannelParticipantCreator::Flag;
|
||||
_additional.applyParticipant(MTP_channelParticipantCreator(
|
||||
MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank),
|
||||
MTP_int(user->bareId()),
|
||||
peerToBareMTPInt(user->id),
|
||||
rights,
|
||||
MTP_string(rank)));
|
||||
} else if (rights.c_chatAdminRights().vflags().v == 0) {
|
||||
_additional.applyParticipant(MTP_channelParticipant(
|
||||
MTP_int(user->bareId()),
|
||||
peerToBareMTPInt(user->id),
|
||||
MTP_int(date)));
|
||||
if (_role == Role::Admins) {
|
||||
removeRow(user);
|
||||
|
|
@ -1607,11 +1609,11 @@ void ParticipantsBoxController::editAdminDone(
|
|||
_additional.applyParticipant(MTP_channelParticipantAdmin(
|
||||
MTP_flags(Flag::f_can_edit
|
||||
| (rank.isEmpty() ? Flag(0) : Flag::f_rank)),
|
||||
MTP_int(user->bareId()),
|
||||
peerToBareMTPInt(user->id),
|
||||
MTPint(), // inviter_id
|
||||
MTP_int(alreadyPromotedBy
|
||||
? alreadyPromotedBy->bareId()
|
||||
: user->session().userId()),
|
||||
peerToBareMTPInt(alreadyPromotedBy
|
||||
? alreadyPromotedBy->id
|
||||
: user->session().userPeerId()),
|
||||
MTP_int(date),
|
||||
rights,
|
||||
MTP_string(rank)));
|
||||
|
|
@ -1667,7 +1669,7 @@ void ParticipantsBoxController::editRestrictedDone(
|
|||
if (Data::ChatBannedRightsFlags(rights) == 0) {
|
||||
if (user) {
|
||||
_additional.applyParticipant(MTP_channelParticipant(
|
||||
MTP_int(user->bareId()),
|
||||
peerToBareMTPInt(user->id),
|
||||
MTP_int(date)));
|
||||
} else {
|
||||
_additional.setExternal(participant);
|
||||
|
|
@ -1684,14 +1686,10 @@ void ParticipantsBoxController::editRestrictedDone(
|
|||
MTP_flags(kicked
|
||||
? MTPDchannelParticipantBanned::Flag::f_left
|
||||
: MTPDchannelParticipantBanned::Flag(0)),
|
||||
(participant->isUser()
|
||||
? MTP_peerUser(MTP_int(participant->bareId()))
|
||||
: participant->isChat()
|
||||
? MTP_peerChat(MTP_int(participant->bareId()))
|
||||
: MTP_peerChannel(MTP_int(participant->bareId()))),
|
||||
MTP_int(alreadyRestrictedBy
|
||||
? alreadyRestrictedBy->bareId()
|
||||
: participant->session().userId()),
|
||||
peerToMTP(participant->id),
|
||||
peerToBareMTPInt(alreadyRestrictedBy
|
||||
? alreadyRestrictedBy->id
|
||||
: participant->session().userPeerId()),
|
||||
MTP_int(date),
|
||||
rights));
|
||||
if (kicked) {
|
||||
|
|
@ -1734,7 +1732,7 @@ void ParticipantsBoxController::kickParticipant(not_null<PeerData*> participant)
|
|||
|
||||
void ParticipantsBoxController::unkickParticipant(not_null<UserData*> user) {
|
||||
_editBox = nullptr;
|
||||
if (const auto row = delegate()->peerListFindRow(user->id)) {
|
||||
if (const auto row = delegate()->peerListFindRow(user->id.value)) {
|
||||
delegate()->peerListRemoveRow(row);
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
|
|
@ -1750,7 +1748,7 @@ void ParticipantsBoxController::kickParticipantSure(
|
|||
? *restrictedRights
|
||||
: ChannelData::EmptyRestrictedRights(participant);
|
||||
|
||||
if (const auto row = delegate()->peerListFindRow(participant->id)) {
|
||||
if (const auto row = delegate()->peerListFindRow(participant->id.value)) {
|
||||
delegate()->peerListRemoveRow(row);
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
|
|
@ -1800,7 +1798,7 @@ void ParticipantsBoxController::removeAdminSure(not_null<UserData*> user) {
|
|||
|
||||
void ParticipantsBoxController::removeKickedWithRow(
|
||||
not_null<PeerData*> participant) {
|
||||
if (const auto row = delegate()->peerListFindRow(participant->id)) {
|
||||
if (const auto row = delegate()->peerListFindRow(participant->id.value)) {
|
||||
removeKicked(row, participant);
|
||||
} else {
|
||||
removeKicked(participant);
|
||||
|
|
@ -1826,7 +1824,7 @@ void ParticipantsBoxController::removeKicked(
|
|||
}
|
||||
|
||||
bool ParticipantsBoxController::appendRow(not_null<PeerData*> participant) {
|
||||
if (delegate()->peerListFindRow(participant->id)) {
|
||||
if (delegate()->peerListFindRow(participant->id.value)) {
|
||||
recomputeTypeFor(participant);
|
||||
return false;
|
||||
} else if (auto row = createRow(participant)) {
|
||||
|
|
@ -1840,7 +1838,7 @@ bool ParticipantsBoxController::appendRow(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);
|
||||
refreshCustomStatus(row);
|
||||
if (_role == Role::Admins) {
|
||||
|
|
@ -1859,7 +1857,7 @@ bool ParticipantsBoxController::prependRow(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) {
|
||||
// Perhaps we are removing an admin from search results.
|
||||
row->setCustomStatus(tr::lng_channel_admin_status_not_admin(tr::now));
|
||||
|
|
@ -1945,7 +1943,7 @@ void ParticipantsBoxController::recomputeTypeFor(
|
|||
if (_role != Role::Profile) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "main/main_session.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/single_choice_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/peers/edit_participants_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_linked_chat_box.h"
|
||||
#include "boxes/stickers_box.h"
|
||||
#include "ui/boxes/single_choice_box.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
|
|
|
|||
|
|
@ -766,7 +766,7 @@ void AdminsController::prepare() {
|
|||
owner.processUsers(data.vusers());
|
||||
for (const auto &admin : data.vadmins().v) {
|
||||
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 (!user->isSelf()) {
|
||||
appendRow(user, data.vinvites_count().v);
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ void ShareBox::prepare() {
|
|||
applyFilterUpdate(query);
|
||||
});
|
||||
_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);
|
||||
selectedChanged();
|
||||
update();
|
||||
|
|
@ -610,7 +610,7 @@ void ShareBox::addPeerToMultiSelect(PeerData *peer, bool skipAnimation) {
|
|||
using AddItemWay = Ui::MultiSelect::AddItemWay;
|
||||
auto addItemWay = skipAnimation ? AddItemWay::SkipAnimation : AddItemWay::Default;
|
||||
_select->addItem(
|
||||
peer->id,
|
||||
peer->id.value,
|
||||
peer->isSelf() ? tr::lng_saved_short(tr::now) : peer->shortName(),
|
||||
st::activeButtonBg,
|
||||
PaintUserpicCallback(peer, true),
|
||||
|
|
@ -622,7 +622,7 @@ void ShareBox::innerSelectedChanged(PeerData *peer, bool checked) {
|
|||
addPeerToMultiSelect(peer);
|
||||
//_select->clearQuery();
|
||||
} else {
|
||||
_select->removeItem(peer->id);
|
||||
_select->removeItem(peer->id.value);
|
||||
}
|
||||
selectedChanged();
|
||||
update();
|
||||
|
|
@ -1285,25 +1285,25 @@ QString AppendShareGameScoreUrl(
|
|||
not_null<Main::Session*> session,
|
||||
const QString &url,
|
||||
const FullMsgId &fullId) {
|
||||
auto shareHashData = QByteArray(0x10, Qt::Uninitialized);
|
||||
auto shareHashDataInts = reinterpret_cast<int32*>(shareHashData.data());
|
||||
auto shareHashData = QByteArray(0x20, Qt::Uninitialized);
|
||||
auto shareHashDataInts = reinterpret_cast<uint64*>(shareHashData.data());
|
||||
auto channel = fullId.channel
|
||||
? session->data().channelLoaded(fullId.channel)
|
||||
: static_cast<ChannelData*>(nullptr);
|
||||
auto channelAccessHash = channel ? channel->access : 0ULL;
|
||||
auto channelAccessHash = uint64(channel ? channel->access : 0);
|
||||
auto channelAccessHashInts = reinterpret_cast<int32*>(&channelAccessHash);
|
||||
shareHashDataInts[0] = session->userId();
|
||||
shareHashDataInts[1] = fullId.channel;
|
||||
shareHashDataInts[0] = session->userId().bare;
|
||||
shareHashDataInts[1] = fullId.channel.bare;
|
||||
shareHashDataInts[2] = fullId.msg;
|
||||
shareHashDataInts[3] = channelAccessHashInts[0];
|
||||
shareHashDataInts[3] = channelAccessHash;
|
||||
|
||||
// Count SHA1() of data.
|
||||
auto key128Size = 0x10;
|
||||
auto shareHashEncrypted = QByteArray(key128Size + shareHashData.size(), Qt::Uninitialized);
|
||||
hashSha1(shareHashData.constData(), shareHashData.size(), shareHashEncrypted.data());
|
||||
|
||||
// Mix in channel access hash to the first 64 bits of SHA1 of data.
|
||||
*reinterpret_cast<uint64*>(shareHashEncrypted.data()) ^= *reinterpret_cast<uint64*>(channelAccessHashInts);
|
||||
//// Mix in channel access hash to the first 64 bits of SHA1 of data.
|
||||
//*reinterpret_cast<uint64*>(shareHashEncrypted.data()) ^= channelAccessHash;
|
||||
|
||||
// Encrypt data.
|
||||
if (!session->local().encrypt(shareHashData.constData(), shareHashEncrypted.data() + key128Size, shareHashData.size(), shareHashEncrypted.constData())) {
|
||||
|
|
@ -1335,7 +1335,7 @@ void ShareGameScoreByHash(
|
|||
auto key128Size = 0x10;
|
||||
|
||||
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)));
|
||||
return;
|
||||
}
|
||||
|
|
@ -1350,37 +1350,46 @@ void ShareGameScoreByHash(
|
|||
char dataSha1[20] = { 0 };
|
||||
hashSha1(hashData.constData(), hashData.size(), dataSha1);
|
||||
|
||||
// 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);
|
||||
//// 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);
|
||||
|
||||
// Check next 64 bits of SHA1() of data.
|
||||
auto skipSha1Part = sizeof(channelAccessHash);
|
||||
if (memcmp(dataSha1 + skipSha1Part, hashEncrypted.constData() + skipSha1Part, key128Size - skipSha1Part) != 0) {
|
||||
//// Check next 64 bits of SHA1() of data.
|
||||
//auto skipSha1Part = sizeof(channelAccessHash);
|
||||
//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)));
|
||||
return;
|
||||
}
|
||||
|
||||
auto hashDataInts = reinterpret_cast<int32*>(hashData.data());
|
||||
if (hashDataInts[0] != session->userId()) {
|
||||
auto hashDataInts = reinterpret_cast<uint64*>(hashData.data());
|
||||
if (hashDataInts[0] != session->userId().bare) {
|
||||
Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check first 32 bits of channel access hash.
|
||||
auto channelAccessHashInts = reinterpret_cast<int32*>(&channelAccessHash);
|
||||
if (channelAccessHashInts[0] != hashDataInts[3]) {
|
||||
Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now)));
|
||||
return;
|
||||
}
|
||||
auto channelAccessHash = hashDataInts[3];
|
||||
//auto channelAccessHashInts = reinterpret_cast<int32*>(&channelAccessHash);
|
||||
//if (channelAccessHashInts[0] != hashDataInts[3]) {
|
||||
// Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now)));
|
||||
// return;
|
||||
//}
|
||||
|
||||
auto channelId = hashDataInts[1];
|
||||
auto msgId = hashDataInts[2];
|
||||
if (!channelId && channelAccessHash) {
|
||||
if (((hashDataInts[1] >> 40) != 0)
|
||||
|| ((hashDataInts[2] >> 32) != 0)
|
||||
|| (!hashDataInts[1] && channelAccessHash)) {
|
||||
// If there is no channel id, there should be no channel access_hash.
|
||||
Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now)));
|
||||
return;
|
||||
}
|
||||
|
||||
auto channelId = ChannelId(hashDataInts[1]);
|
||||
auto msgId = MsgId(hashDataInts[2]);
|
||||
if (const auto item = session->data().message(channelId, msgId)) {
|
||||
FastShareMessage(item);
|
||||
} else {
|
||||
|
|
@ -1406,7 +1415,7 @@ void ShareGameScoreByHash(
|
|||
MTP_vector<MTPInputChannel>(
|
||||
1,
|
||||
MTP_inputChannel(
|
||||
MTP_int(channelId),
|
||||
MTP_int(channelId.bare), // #TODO ids
|
||||
MTP_long(channelAccessHash)))
|
||||
)).done([=](const MTPmessages_Chats &result) {
|
||||
result.match([&](const auto &data) {
|
||||
|
|
|
|||
|
|
@ -393,8 +393,8 @@ callErrorToast: Toast(defaultToast) {
|
|||
groupCallWidth: 380px;
|
||||
groupCallHeight: 580px;
|
||||
|
||||
groupCallMuteButtonIconSize: size(55px, 55px);
|
||||
groupCallMuteButtonIconTop: 42px;
|
||||
groupCallMuteButtonIconSize: size(67px, 67px);
|
||||
groupCallMuteButtonIconTop: 35px;
|
||||
groupCallRipple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: groupCallMembersBgRipple;
|
||||
}
|
||||
|
|
@ -685,13 +685,19 @@ groupCallMemberInvited: icon {{ "calls/group_calls_invited", groupCallMemberInac
|
|||
groupCallMemberInvitedPosition: point(2px, 12px);
|
||||
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) {
|
||||
button: IconButton(callButton) {
|
||||
iconPosition: point(-1px, 22px);
|
||||
icon: icon {{ "calls/call_settings", groupCallIconFg }};
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: callMuteRipple;
|
||||
}
|
||||
button: groupCallSettingsInner;
|
||||
}
|
||||
groupCallShare: CallButton(groupCallSettings) {
|
||||
button: IconButton(groupCallSettingsInner) {
|
||||
icon: icon {{ "calls/group_calls_share", groupCallIconFg }};
|
||||
}
|
||||
}
|
||||
groupCallHangup: CallButton(callHangup) {
|
||||
|
|
@ -720,6 +726,11 @@ groupCallTopBarJoin: RoundButton(defaultActiveButton) {
|
|||
height: 26px;
|
||||
textTop: 4px;
|
||||
}
|
||||
groupCallTopBarOpen: RoundButton(groupCallTopBarJoin) {
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: shadowFg;
|
||||
}
|
||||
}
|
||||
groupCallBox: Box(defaultBox) {
|
||||
button: RoundButton(defaultBoxButton) {
|
||||
textFg: groupCallActiveFg;
|
||||
|
|
@ -940,3 +951,18 @@ callTopBarMuteCrossLine: CrossLineAnimation {
|
|||
endPosition: point(26px, 23px);
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -409,7 +409,8 @@ void BoxController::receivedCalls(const QVector<MTPMessage> &result) {
|
|||
NewMessageType::Existing);
|
||||
insertRow(item, InsertWay::Append);
|
||||
} 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -490,13 +490,13 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
|
|||
auto &data = call.c_phoneCallRequested();
|
||||
if (_type != Type::Incoming
|
||||
|| _id != 0
|
||||
|| peerToUser(_user->id) != data.vadmin_id().v) {
|
||||
|| peerToUser(_user->id) != UserId(data.vadmin_id())) {
|
||||
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."
|
||||
).arg(data.vparticipant_id().v
|
||||
).arg(_user->session().userId()));
|
||||
).arg(_user->session().userId().bare));
|
||||
finish(FinishType::Failed);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -891,12 +891,12 @@ bool Call::checkCallCommonFields(const T &call) {
|
|||
}
|
||||
auto adminId = (_type == Type::Outgoing) ? _user->session().userId() : peerToUser(_user->id);
|
||||
auto participantId = (_type == Type::Outgoing) ? peerToUser(_user->id) : _user->session().userId();
|
||||
if (call.vadmin_id().v != adminId) {
|
||||
LOG(("Call Error: Wrong call admin_id %1, expected %2.").arg(call.vadmin_id().v).arg(adminId));
|
||||
if (UserId(call.vadmin_id()) != adminId) {
|
||||
LOG(("Call Error: Wrong call admin_id %1, expected %2.").arg(call.vadmin_id().v).arg(adminId.bare));
|
||||
return checkFailed();
|
||||
}
|
||||
if (call.vparticipant_id().v != participantId) {
|
||||
LOG(("Call Error: Wrong call participant_id %1, expected %2.").arg(call.vparticipant_id().v).arg(participantId));
|
||||
if (UserId(call.vparticipant_id()) != participantId) {
|
||||
LOG(("Call Error: Wrong call participant_id %1, expected %2.").arg(call.vparticipant_id().v).arg(participantId.bare));
|
||||
return checkFailed();
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "calls/calls_choose_join_as.h"
|
||||
|
||||
#include "calls/calls_group_common.h"
|
||||
#include "calls/calls_group_menu.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_channel.h"
|
||||
|
|
@ -18,15 +19,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "lang/lang_keys.h"
|
||||
#include "apiwrap.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/boxes/choose_date_time.h"
|
||||
#include "ui/text/text_utilities.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_layers.h"
|
||||
#include "styles/style_calls.h"
|
||||
|
||||
namespace Calls::Group {
|
||||
namespace {
|
||||
|
||||
constexpr auto kDefaultScheduleDuration = 60 * TimeId(60);
|
||||
constexpr auto kLabelRefreshInterval = 10 * crl::time(1000);
|
||||
|
||||
using Context = ChooseJoinAsProcess::Context;
|
||||
|
||||
class ListController : public PeerListController {
|
||||
|
|
@ -98,7 +105,7 @@ void ListController::rowClicked(not_null<PeerListRow*> row) {
|
|||
if (peer == _selected) {
|
||||
return;
|
||||
}
|
||||
const auto previous = delegate()->peerListFindRow(_selected->id);
|
||||
const auto previous = delegate()->peerListFindRow(_selected->id.value);
|
||||
Assert(previous != nullptr);
|
||||
delegate()->peerListSetRowChecked(previous, false);
|
||||
delegate()->peerListSetRowChecked(row, true);
|
||||
|
|
@ -109,6 +116,79 @@ not_null<PeerData*> ListController::selected() const {
|
|||
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(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
Context context,
|
||||
|
|
@ -124,12 +204,13 @@ void ChooseJoinAsBox(
|
|||
}
|
||||
Unexpected("Context in ChooseJoinAsBox.");
|
||||
}());
|
||||
const auto &labelSt = (context == Context::Switch)
|
||||
? st::groupCallJoinAsLabel
|
||||
: st::confirmPhoneAboutLabel;
|
||||
box->addRow(object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_group_call_join_as_about(),
|
||||
(context == Context::Switch
|
||||
? st::groupCallJoinAsLabel
|
||||
: st::confirmPhoneAboutLabel)));
|
||||
labelSt));
|
||||
|
||||
auto &lifetime = box->lifetime();
|
||||
const auto delegate = lifetime.make_state<
|
||||
|
|
@ -155,6 +236,25 @@ void ChooseJoinAsBox(
|
|||
auto next = (context == Context::Switch)
|
||||
? tr::lng_settings_save()
|
||||
: 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), [=] {
|
||||
auto copy = info;
|
||||
copy.joinAs = controller->selected();
|
||||
|
|
@ -296,7 +396,7 @@ void ChooseJoinAsProcess::start(
|
|||
&& (peer->groupCall() != nullptr);
|
||||
|
||||
if (!changingJoinAsFrom && (onlyByMe || byAlreadyUsed)) {
|
||||
const auto confirmation = CreateOrJoinConfirmation(
|
||||
auto confirmation = CreateOrJoinConfirmation(
|
||||
peer,
|
||||
context,
|
||||
byAlreadyUsed);
|
||||
|
|
@ -304,12 +404,36 @@ void ChooseJoinAsProcess::start(
|
|||
finish(info);
|
||||
return;
|
||||
}
|
||||
auto box = Box<::ConfirmBox>(
|
||||
confirmation,
|
||||
(peer->groupCall()
|
||||
? tr::lng_group_call_join(tr::now)
|
||||
: tr::lng_create_group_create(tr::now)),
|
||||
crl::guard(&_request->guard, [=] { finish(info); }));
|
||||
const auto creating = !peer->groupCall();
|
||||
if (creating) {
|
||||
confirmation
|
||||
.append("\n\n")
|
||||
.append(tr::lng_group_call_or_schedule(
|
||||
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(
|
||||
) | rpl::start_with_next([=] {
|
||||
_request = nullptr;
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ private:
|
|||
}
|
||||
if (const auto chat = peer->asChat()) {
|
||||
return chat->admins.contains(user)
|
||||
|| (chat->creator == user->bareId());
|
||||
|| (chat->creator == peerToUser(user->id));
|
||||
} else if (const auto group = peer->asChannel()) {
|
||||
if (const auto mgInfo = group->mgInfo.get()) {
|
||||
if (mgInfo->creator == user) {
|
||||
|
|
@ -184,6 +184,8 @@ GroupCall::GroupCall(
|
|||
, _joinAs(info.joinAs)
|
||||
, _possibleJoinAs(std::move(info.possibleJoinAs))
|
||||
, _joinHash(info.joinHash)
|
||||
, _id(inputCall.c_inputGroupCall().vid().v)
|
||||
, _scheduleDate(info.scheduleDate)
|
||||
, _lastSpokeCheckTimer([=] { checkLastSpoke(); })
|
||||
, _checkJoinedTimer([=] { checkJoined(); })
|
||||
, _pushToTalkCancelTimer([=] { pushToTalkCancel(); })
|
||||
|
|
@ -215,17 +217,35 @@ GroupCall::GroupCall(
|
|||
|
||||
checkGlobalShortcutAvailability();
|
||||
|
||||
const auto id = inputCall.c_inputGroupCall().vid().v;
|
||||
if (id) {
|
||||
if (const auto call = _peer->groupCall(); call && call->id() == id) {
|
||||
if (!_peer->canManageGroupCall() && call->joinMuted()) {
|
||||
_muted = MuteState::ForceMuted;
|
||||
}
|
||||
if (const auto real = lookupReal()) {
|
||||
subscribeToReal(real);
|
||||
if (!_peer->canManageGroupCall() && real->joinMuted()) {
|
||||
_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);
|
||||
} else {
|
||||
start();
|
||||
start(info.scheduleDate);
|
||||
}
|
||||
if (_scheduleDate) {
|
||||
saveDefaultJoinAs(_joinAs);
|
||||
}
|
||||
|
||||
_mediaDevices->audioInputId(
|
||||
|
|
@ -249,6 +269,17 @@ GroupCall::~GroupCall() {
|
|||
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() {
|
||||
auto &settings = Core::App().settings();
|
||||
if (!settings.groupCallPushToTalk()) {
|
||||
|
|
@ -326,10 +357,33 @@ bool GroupCall::showChooseJoinAs() const {
|
|||
&& !_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(
|
||||
MTP_flags(scheduleDate ? Flag::f_schedule_date : Flag(0)),
|
||||
_peer->input,
|
||||
MTP_int(openssl::RandomValue<int32>())
|
||||
MTP_int(openssl::RandomValue<int32>()),
|
||||
MTPstring(), // title
|
||||
MTP_int(scheduleDate)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_acceptFields = true;
|
||||
_peer->session().api().applyUpdates(result);
|
||||
|
|
@ -347,20 +401,16 @@ void GroupCall::start() {
|
|||
}
|
||||
|
||||
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) {
|
||||
_id = data.vid().v;
|
||||
_accessHash = data.vaccess_hash().v;
|
||||
rejoin();
|
||||
});
|
||||
setState(_scheduleDate ? State::Waiting : State::Joining);
|
||||
|
||||
if (_scheduleDate) {
|
||||
return;
|
||||
}
|
||||
rejoin();
|
||||
|
||||
using Update = Data::GroupCall::ParticipantUpdate;
|
||||
_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) {
|
||||
if (state() != State::Joining
|
||||
&& state() != State::Joined
|
||||
|
|
@ -424,12 +491,7 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
|||
applyMeInCallLocally();
|
||||
LOG(("Call Info: Requesting join payload."));
|
||||
|
||||
_joinAs = as;
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
chat->setGroupCallDefaultJoinAs(_joinAs->id);
|
||||
} else if (const auto channel = _peer->asChannel()) {
|
||||
channel->setGroupCallDefaultJoinAs(_joinAs->id);
|
||||
}
|
||||
setJoinAs(as);
|
||||
|
||||
const auto weak = base::make_weak(this);
|
||||
_instance->emitJoinPayload([=](tgcalls::GroupJoinPayload payload) {
|
||||
|
|
@ -479,6 +541,7 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
|||
applyMeInCallLocally();
|
||||
maybeSendMutedUpdate(wasMuteState);
|
||||
_peer->session().api().applyUpdates(updates);
|
||||
applyQueuedSelfUpdates();
|
||||
checkFirstTimeJoined();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
const auto type = error.type();
|
||||
|
|
@ -561,7 +624,8 @@ void GroupCall::applyMeInCallLocally() {
|
|||
MTP_int(_mySsrc),
|
||||
MTP_int(volume),
|
||||
MTPstring(), // Don't update about text in local updates.
|
||||
MTP_long(raisedHandRating))),
|
||||
MTP_long(raisedHandRating),
|
||||
MTPDataJSON())),
|
||||
MTP_int(0)).c_updateGroupCallParticipants());
|
||||
}
|
||||
|
||||
|
|
@ -606,7 +670,8 @@ void GroupCall::applyParticipantLocally(
|
|||
MTP_int(participant->ssrc),
|
||||
MTP_int(volume.value_or(participant->volume)),
|
||||
MTPstring(), // Don't update about text in local updates.
|
||||
MTP_long(participant->raisedHandRating))),
|
||||
MTP_long(participant->raisedHandRating),
|
||||
MTPDataJSON())),
|
||||
MTP_int(0)).c_updateGroupCallParticipants());
|
||||
}
|
||||
|
||||
|
|
@ -641,8 +706,12 @@ void GroupCall::rejoinAs(Group::JoinInfo info) {
|
|||
.wasJoinAs = _joinAs,
|
||||
.nowJoinAs = info.joinAs,
|
||||
};
|
||||
setState(State::Joining);
|
||||
rejoin(info.joinAs);
|
||||
if (_scheduleDate) {
|
||||
saveDefaultJoinAs(info.joinAs);
|
||||
} else {
|
||||
setState(State::Joining);
|
||||
rejoin(info.joinAs);
|
||||
}
|
||||
_rejoinEvents.fire_copy(event);
|
||||
}
|
||||
|
||||
|
|
@ -686,6 +755,29 @@ void GroupCall::finish(FinishType type) {
|
|||
})).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) {
|
||||
const auto set = [=] {
|
||||
const auto wasMuted = (muted() == MuteState::Muted)
|
||||
|
|
@ -729,9 +821,25 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
|
|||
|
||||
void GroupCall::handlePossibleCreateOrJoinResponse(
|
||||
const MTPDgroupCall &data) {
|
||||
if (const auto date = data.vschedule_date()) {
|
||||
_scheduleDate = date->v;
|
||||
} else {
|
||||
_scheduleDate = 0;
|
||||
}
|
||||
if (_acceptFields) {
|
||||
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;
|
||||
} else if (_id != data.vid().v || !_instance) {
|
||||
|
|
@ -825,10 +933,8 @@ void GroupCall::handlePossibleDiscarded(const MTPDgroupCallDiscarded &data) {
|
|||
}
|
||||
|
||||
void GroupCall::addParticipantsToInstance() {
|
||||
const auto real = _peer->groupCall();
|
||||
if (!real
|
||||
|| (real->id() != _id)
|
||||
|| (_instanceMode == InstanceMode::None)) {
|
||||
const auto real = lookupReal();
|
||||
if (!real || (_instanceMode == InstanceMode::None)) {
|
||||
return;
|
||||
}
|
||||
for (const auto &participant : real->participants()) {
|
||||
|
|
@ -850,7 +956,7 @@ void GroupCall::addPreparedParticipants() {
|
|||
if (!_preparedParticipants.empty()) {
|
||||
_instance->addParticipants(base::take(_preparedParticipants));
|
||||
}
|
||||
if (const auto real = _peer->groupCall(); real && real->id() == _id) {
|
||||
if (const auto real = lookupReal()) {
|
||||
if (!_unresolvedSsrcs.empty()) {
|
||||
real->resolveParticipants(base::take(_unresolvedSsrcs));
|
||||
}
|
||||
|
|
@ -890,91 +996,105 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
|
|||
return;
|
||||
}
|
||||
const auto state = _state.current();
|
||||
if (state != State::Joined && state != State::Connecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
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(),
|
||||
});
|
||||
};
|
||||
|
||||
const auto joined = (state == State::Joined)
|
||||
|| (state == State::Connecting);
|
||||
for (const auto &participant : data.vparticipants().v) {
|
||||
participant.match([&](const MTPDgroupCallParticipant &data) {
|
||||
const auto isSelf = data.is_self()
|
||||
|| (data.is_min()
|
||||
&& peerFromMTP(data.vpeer()) == _joinAs->id);
|
||||
if (!isSelf) {
|
||||
handleOtherParticipants(data);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
applyOtherParticipantUpdate(data);
|
||||
} else if (joined) {
|
||||
applySelfUpdate(data);
|
||||
} else {
|
||||
_queuedSelfUpdates.push_back(participant);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
const auto real = _peer->groupCall();
|
||||
if (!real || real->id() != _id || real->title() == title) {
|
||||
const auto real = lookupReal();
|
||||
if (!real || real->title() == title) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -989,8 +1109,8 @@ void GroupCall::changeTitle(const QString &title) {
|
|||
}
|
||||
|
||||
void GroupCall::toggleRecording(bool enabled, const QString &title) {
|
||||
const auto real = _peer->groupCall();
|
||||
if (!real || real->id() != _id) {
|
||||
const auto real = lookupReal();
|
||||
if (!real) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1169,10 +1289,8 @@ void GroupCall::broadcastPartCancel(not_null<LoadPartTask*> task) {
|
|||
|
||||
void GroupCall::requestParticipantsInformation(
|
||||
const std::vector<uint32_t> &ssrcs) {
|
||||
const auto real = _peer->groupCall();
|
||||
if (!real
|
||||
|| (real->id() != _id)
|
||||
|| (_instanceMode == InstanceMode::None)) {
|
||||
const auto real = lookupReal();
|
||||
if (!real || (_instanceMode == InstanceMode::None)) {
|
||||
for (const auto ssrc : ssrcs) {
|
||||
_unresolvedSsrcs.emplace(ssrc);
|
||||
}
|
||||
|
|
@ -1206,8 +1324,8 @@ void GroupCall::updateInstanceMuteState() {
|
|||
}
|
||||
|
||||
void GroupCall::updateInstanceVolumes() {
|
||||
const auto real = _peer->groupCall();
|
||||
if (!real || real->id() != _id) {
|
||||
const auto real = lookupReal();
|
||||
if (!real) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1283,8 +1401,8 @@ void GroupCall::audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data) {
|
|||
}
|
||||
|
||||
void GroupCall::checkLastSpoke() {
|
||||
const auto real = _peer->groupCall();
|
||||
if (!real || real->id() != _id) {
|
||||
const auto real = lookupReal();
|
||||
if (!real) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1495,8 +1613,8 @@ void GroupCall::editParticipant(
|
|||
|
||||
std::variant<int, not_null<UserData*>> GroupCall::inviteUsers(
|
||||
const std::vector<not_null<UserData*>> &users) {
|
||||
const auto real = _peer->groupCall();
|
||||
if (!real || real->id() != _id) {
|
||||
const auto real = lookupReal();
|
||||
if (!real) {
|
||||
return 0;
|
||||
}
|
||||
const auto owner = &_peer->owner();
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class MediaDevices;
|
|||
namespace Data {
|
||||
struct LastSpokeTimes;
|
||||
struct GroupCallParticipant;
|
||||
class GroupCall;
|
||||
} // namespace Data
|
||||
|
||||
namespace Calls {
|
||||
|
|
@ -109,8 +110,15 @@ public:
|
|||
return _joinAs;
|
||||
}
|
||||
[[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 discard();
|
||||
void rejoinAs(Group::JoinInfo info);
|
||||
|
|
@ -123,6 +131,8 @@ public:
|
|||
[[nodiscard]] bool recordingStoppedByMe() const {
|
||||
return _recordingStoppedByMe;
|
||||
}
|
||||
void startScheduledNow();
|
||||
void toggleScheduleStartSubscribed(bool subscribed);
|
||||
|
||||
void setMuted(MuteState mute);
|
||||
void setMutedAndUpdate(MuteState mute);
|
||||
|
|
@ -138,6 +148,7 @@ public:
|
|||
|
||||
enum State {
|
||||
Creating,
|
||||
Waiting,
|
||||
Joining,
|
||||
Connecting,
|
||||
Joined,
|
||||
|
|
@ -245,6 +256,9 @@ private:
|
|||
void applyMeInCallLocally();
|
||||
void rejoin();
|
||||
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 setInstanceConnected(tgcalls::GroupNetworkState networkState);
|
||||
|
|
@ -276,6 +290,9 @@ private:
|
|||
not_null<PeerData*> participantPeer,
|
||||
bool mute,
|
||||
std::optional<int> volume);
|
||||
void applyQueuedSelfUpdates();
|
||||
void applySelfUpdate(const MTPDgroupCallParticipant &data);
|
||||
void applyOtherParticipantUpdate(const MTPDgroupCallParticipant &data);
|
||||
|
||||
[[nodiscard]] MTPInputGroupCall inputCall() const;
|
||||
|
||||
|
|
@ -284,6 +301,7 @@ private:
|
|||
rpl::event_stream<PeerData*> _peerStream;
|
||||
not_null<History*> _history; // Can change in legacy group migration.
|
||||
MTP::Sender _api;
|
||||
rpl::event_stream<not_null<Data::GroupCall*>> _realChanges;
|
||||
rpl::variable<State> _state = State::Creating;
|
||||
rpl::variable<InstanceState> _instanceState
|
||||
= InstanceState::Disconnected;
|
||||
|
|
@ -306,10 +324,12 @@ private:
|
|||
bool _acceptFields = false;
|
||||
|
||||
rpl::event_stream<Group::ParticipantState> _otherParticipantStateValue;
|
||||
std::vector<MTPGroupCallParticipant> _queuedSelfUpdates;
|
||||
|
||||
uint64 _id = 0;
|
||||
uint64 _accessHash = 0;
|
||||
uint32 _mySsrc = 0;
|
||||
TimeId _scheduleDate = 0;
|
||||
base::flat_set<uint32> _mySsrcs;
|
||||
mtpRequestId _createRequestId = 0;
|
||||
mtpRequestId _updateMuteRequestId = 0;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ struct JoinInfo {
|
|||
not_null<PeerData*> joinAs;
|
||||
std::vector<not_null<PeerData*>> possibleJoinAs;
|
||||
QString joinHash;
|
||||
TimeId scheduleDate = 0;
|
||||
};
|
||||
|
||||
} // namespace Calls::Group
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ private:
|
|||
not_null<PeerData*> participantPeer,
|
||||
bool participantIsCallAdmin,
|
||||
not_null<Row*> row);
|
||||
void setupListChangeViewers(not_null<GroupCall*> call);
|
||||
void setupListChangeViewers();
|
||||
void subscribeToChanges(not_null<Data::GroupCall*> real);
|
||||
void updateRow(
|
||||
const std::optional<Data::GroupCall::Participant> &was,
|
||||
|
|
@ -335,16 +335,11 @@ private:
|
|||
uint64 raiseHandRating) const;
|
||||
Row *findRow(not_null<PeerData*> participantPeer) const;
|
||||
|
||||
[[nodiscard]] Data::GroupCall *resolvedRealCall() const;
|
||||
void appendInvitedUsers();
|
||||
void scheduleRaisedHandStatusRemove();
|
||||
|
||||
const base::weak_ptr<GroupCall> _call;
|
||||
const not_null<GroupCall*> _call;
|
||||
not_null<PeerData*> _peer;
|
||||
|
||||
// Use only resolvedRealCall() method, not this value directly.
|
||||
Data::GroupCall *_realCallRawValue = nullptr;
|
||||
uint64 _realId = 0;
|
||||
bool _prepared = false;
|
||||
|
||||
rpl::event_stream<MuteRequest> _toggleMuteRequests;
|
||||
|
|
@ -909,7 +904,7 @@ MembersController::MembersController(
|
|||
, _raisedHandStatusRemoveTimer([=] { scheduleRaisedHandStatusRemove(); })
|
||||
, _inactiveCrossLine(st::groupCallMemberInactiveCrossLine)
|
||||
, _coloredCrossLine(st::groupCallMemberColoredCrossLine) {
|
||||
setupListChangeViewers(call);
|
||||
setupListChangeViewers();
|
||||
|
||||
style::PaletteChanged(
|
||||
) | rpl::start_with_next([=] {
|
||||
|
|
@ -964,32 +959,20 @@ MembersController::~MembersController() {
|
|||
base::take(_menu);
|
||||
}
|
||||
|
||||
void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
|
||||
const auto peer = call->peer();
|
||||
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
|
||||
void MembersController::setupListChangeViewers() {
|
||||
_call->real(
|
||||
) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) {
|
||||
subscribeToChanges(real);
|
||||
}, _lifetime);
|
||||
|
||||
call->stateValue(
|
||||
_call->stateValue(
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto call = _call.get();
|
||||
const auto real = peer->groupCall();
|
||||
if (call && real && (real->id() == call->id())) {
|
||||
if (const auto real = _call->lookupReal()) {
|
||||
//updateRow(channel->session().user());
|
||||
}
|
||||
}, _lifetime);
|
||||
|
||||
call->levelUpdates(
|
||||
_call->levelUpdates(
|
||||
) | rpl::start_with_next([=](const LevelUpdate &update) {
|
||||
const auto i = _soundingRowBySsrc.find(update.ssrc);
|
||||
if (i != end(_soundingRowBySsrc)) {
|
||||
|
|
@ -997,7 +980,7 @@ void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
|
|||
}
|
||||
}, _lifetime);
|
||||
|
||||
call->rejoinEvents(
|
||||
_call->rejoinEvents(
|
||||
) | rpl::start_with_next([=](const Group::RejoinEvent &event) {
|
||||
const auto guard = gsl::finally([&] {
|
||||
delegate()->peerListRefreshRows();
|
||||
|
|
@ -1014,9 +997,6 @@ void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
|
|||
}
|
||||
|
||||
void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
||||
_realCallRawValue = real;
|
||||
_realId = real->id();
|
||||
|
||||
_fullCount = real->fullCountValue();
|
||||
|
||||
real->participantsSliceAdded(
|
||||
|
|
@ -1053,17 +1033,19 @@ void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
|||
}
|
||||
|
||||
void MembersController::appendInvitedUsers() {
|
||||
for (const auto user : _peer->owner().invitedToCallUsers(_realId)) {
|
||||
if (auto row = createInvitedRow(user)) {
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
if (const auto id = _call->id()) {
|
||||
for (const auto user : _peer->owner().invitedToCallUsers(id)) {
|
||||
if (auto row = createInvitedRow(user)) {
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
}
|
||||
}
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
delegate()->peerListRefreshRows();
|
||||
|
||||
using Invite = Data::Session::InviteToCall;
|
||||
_peer->owner().invitesToCalls(
|
||||
) | rpl::filter([=](const Invite &invite) {
|
||||
return (invite.id == _realId);
|
||||
return (invite.id == _call->id());
|
||||
}) | rpl::start_with_next([=](const Invite &invite) {
|
||||
if (auto row = createInvitedRow(invite.user)) {
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
|
|
@ -1120,7 +1102,7 @@ void MembersController::updateRow(
|
|||
if (checkPosition) {
|
||||
checkRowPosition(checkPosition);
|
||||
} else if (addedToBottom) {
|
||||
const auto real = resolvedRealCall();
|
||||
const auto real = _call->lookupReal();
|
||||
if (real && real->joinedToTop()) {
|
||||
const auto proj = [&](const PeerListRow &other) {
|
||||
const auto &real = static_cast<const Row&>(other);
|
||||
|
|
@ -1311,15 +1293,7 @@ void MembersController::updateRowLevel(
|
|||
|
||||
Row *MembersController::findRow(not_null<PeerData*> participantPeer) const {
|
||||
return static_cast<Row*>(
|
||||
delegate()->peerListFindRow(participantPeer->id));
|
||||
}
|
||||
|
||||
Data::GroupCall *MembersController::resolvedRealCall() const {
|
||||
return (_realCallRawValue
|
||||
&& (_peer->groupCall() == _realCallRawValue)
|
||||
&& (_realCallRawValue->id() == _realId))
|
||||
? _realCallRawValue
|
||||
: nullptr;
|
||||
delegate()->peerListFindRow(participantPeer->id.value));
|
||||
}
|
||||
|
||||
Main::Session &MembersController::session() const {
|
||||
|
|
@ -1332,9 +1306,7 @@ void MembersController::prepare() {
|
|||
setDescriptionText(tr::lng_contacts_loading(tr::now));
|
||||
setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
|
||||
|
||||
const auto call = _call.get();
|
||||
if (const auto real = _peer->groupCall()
|
||||
; real && call && real->id() == call->id()) {
|
||||
if (const auto real = _call->lookupReal()) {
|
||||
prepareRows(real);
|
||||
} else if (auto row = createRowForMe()) {
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
|
|
@ -1342,15 +1314,12 @@ void MembersController::prepare() {
|
|||
}
|
||||
|
||||
loadMoreRows();
|
||||
if (_realId) {
|
||||
appendInvitedUsers();
|
||||
}
|
||||
appendInvitedUsers();
|
||||
_prepared = true;
|
||||
}
|
||||
|
||||
bool MembersController::isMe(not_null<PeerData*> participantPeer) const {
|
||||
const auto call = _call.get();
|
||||
return call && (call->joinAs() == participantPeer);
|
||||
return (_call->joinAs() == participantPeer);
|
||||
}
|
||||
|
||||
void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
|
||||
|
|
@ -1379,19 +1348,17 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
|
|||
}
|
||||
}
|
||||
if (!foundMe) {
|
||||
if (const auto call = _call.get()) {
|
||||
const auto me = call->joinAs();
|
||||
const auto i = ranges::find(
|
||||
participants,
|
||||
me,
|
||||
&Data::GroupCall::Participant::peer);
|
||||
auto row = (i != end(participants))
|
||||
? createRow(*i)
|
||||
: createRowForMe();
|
||||
if (row) {
|
||||
changed = true;
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
}
|
||||
const auto me = _call->joinAs();
|
||||
const auto i = ranges::find(
|
||||
participants,
|
||||
me,
|
||||
&Data::GroupCall::Participant::peer);
|
||||
auto row = (i != end(participants))
|
||||
? createRow(*i)
|
||||
: createRowForMe();
|
||||
if (row) {
|
||||
changed = true;
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
}
|
||||
}
|
||||
for (const auto &participant : participants) {
|
||||
|
|
@ -1406,7 +1373,7 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
|
|||
}
|
||||
|
||||
void MembersController::loadMoreRows() {
|
||||
if (const auto real = _peer->groupCall()) {
|
||||
if (const auto real = _call->lookupReal()) {
|
||||
real->requestParticipants();
|
||||
}
|
||||
}
|
||||
|
|
@ -1435,7 +1402,7 @@ void MembersController::rowUpdateRow(not_null<Row*> row) {
|
|||
|
||||
void MembersController::rowScheduleRaisedHandStatusRemove(
|
||||
not_null<Row*> row) {
|
||||
const auto id = row->peer()->id;
|
||||
const auto id = row->id();
|
||||
const auto when = crl::now() + kKeepRaisedHandStatusDuration;
|
||||
const auto i = _raisedHandStatusRemoveAt.find(id);
|
||||
if (i != _raisedHandStatusRemoveAt.end()) {
|
||||
|
|
@ -1634,12 +1601,10 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
|
|||
}
|
||||
|
||||
if (isMe(participantPeer)) {
|
||||
if (const auto strong = _call.get()
|
||||
; strong && strong->muted() == MuteState::RaisedHand) {
|
||||
if (_call->muted() == MuteState::RaisedHand) {
|
||||
const auto removeHand = [=] {
|
||||
if (const auto strong = _call.get()
|
||||
; strong && strong->muted() == MuteState::RaisedHand) {
|
||||
strong->setMutedAndUpdate(MuteState::ForceMuted);
|
||||
if (_call->muted() == MuteState::RaisedHand) {
|
||||
_call->setMutedAndUpdate(MuteState::ForceMuted);
|
||||
}
|
||||
};
|
||||
result->addAction(
|
||||
|
|
@ -1728,14 +1693,12 @@ void MembersController::addMuteActionsToContextMenu(
|
|||
|
||||
auto mutesFromVolume = rpl::never<bool>() | rpl::type_erased();
|
||||
|
||||
const auto call = _call.get();
|
||||
if (!isMuted || (call && call->joinAs() == participantPeer)) {
|
||||
auto otherParticipantStateValue = call
|
||||
? call->otherParticipantStateValue(
|
||||
) | rpl::filter([=](const Group::ParticipantState &data) {
|
||||
return data.peer == participantPeer;
|
||||
})
|
||||
: rpl::never<Group::ParticipantState>() | rpl::type_erased();
|
||||
if (!isMuted || _call->joinAs() == participantPeer) {
|
||||
auto otherParticipantStateValue
|
||||
= _call->otherParticipantStateValue(
|
||||
) | rpl::filter([=](const Group::ParticipantState &data) {
|
||||
return data.peer == participantPeer;
|
||||
});
|
||||
|
||||
auto volumeItem = base::make_unique_q<MenuVolumeItem>(
|
||||
menu->menu(),
|
||||
|
|
@ -1814,11 +1777,7 @@ void MembersController::addMuteActionsToContextMenu(
|
|||
}
|
||||
|
||||
std::unique_ptr<Row> MembersController::createRowForMe() {
|
||||
const auto call = _call.get();
|
||||
if (!call) {
|
||||
return nullptr;
|
||||
}
|
||||
auto result = std::make_unique<Row>(this, call->joinAs());
|
||||
auto result = std::make_unique<Row>(this, _call->joinAs());
|
||||
updateRow(result.get(), nullptr);
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1877,12 +1836,8 @@ auto Members::kickParticipantRequests() const
|
|||
int Members::desiredHeight() const {
|
||||
const auto top = _addMember ? _addMember->height() : 0;
|
||||
auto count = [&] {
|
||||
if (const auto call = _call.get()) {
|
||||
if (const auto real = call->peer()->groupCall()) {
|
||||
if (call->id() == real->id()) {
|
||||
return real->fullCount();
|
||||
}
|
||||
}
|
||||
if (const auto real = _call->lookupReal()) {
|
||||
return real->fullCount();
|
||||
}
|
||||
return 0;
|
||||
}();
|
||||
|
|
@ -1911,16 +1866,7 @@ void Members::setupAddMember(not_null<GroupCall*> call) {
|
|||
if (const auto channel = peer->asBroadcast()) {
|
||||
_canAddMembers = rpl::single(
|
||||
false
|
||||
) | rpl::then(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::then(_call->real(
|
||||
) | rpl::map([=] {
|
||||
return Data::PeerFlagValue(
|
||||
channel,
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ private:
|
|||
|
||||
void updateControlsGeometry();
|
||||
|
||||
const base::weak_ptr<GroupCall> _call;
|
||||
const not_null<GroupCall*> _call;
|
||||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
std::unique_ptr<PeerListController> _listController;
|
||||
object_ptr<Ui::SettingsButton> _addMember = { nullptr };
|
||||
|
|
|
|||
|
|
@ -514,15 +514,6 @@ base::unique_qptr<Ui::Menu::ItemBase> MakeRecordingAction(
|
|||
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
|
||||
|
||||
void LeaveBox(
|
||||
|
|
@ -530,16 +521,25 @@ void LeaveBox(
|
|||
not_null<GroupCall*> call,
|
||||
bool discardChecked,
|
||||
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);
|
||||
box->addRow(object_ptr<Ui::FlatLabel>(
|
||||
box.get(),
|
||||
tr::lng_group_call_leave_sure(),
|
||||
(inCall ? st::groupCallBoxLabel : st::boxLabel)));
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box.get(),
|
||||
(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()
|
||||
? box->addRow(object_ptr<Ui::Checkbox>(
|
||||
box.get(),
|
||||
tr::lng_group_call_end(),
|
||||
(scheduled
|
||||
? tr::lng_group_call_also_cancel()
|
||||
: tr::lng_group_call_also_end()),
|
||||
discardChecked,
|
||||
(inCall ? st::groupCallCheckbox : st::defaultBoxCheckbox),
|
||||
(inCall ? st::groupCallCheck : st::defaultCheck)),
|
||||
|
|
@ -550,7 +550,10 @@ void LeaveBox(
|
|||
st::boxRowPadding.bottom()))
|
||||
: nullptr;
|
||||
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());
|
||||
box->closeBox();
|
||||
|
||||
|
|
@ -565,19 +568,20 @@ void LeaveBox(
|
|||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}
|
||||
|
||||
void ConfirmBox(
|
||||
void ConfirmBoxBuilder(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
const TextWithEntities &text,
|
||||
rpl::producer<QString> button,
|
||||
Fn<void()> callback) {
|
||||
box->addRow(
|
||||
ConfirmBoxArgs &&args) {
|
||||
const auto label = box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box.get(),
|
||||
rpl::single(text),
|
||||
st::groupCallBoxLabel),
|
||||
rpl::single(args.text),
|
||||
args.st ? *args.st : st::groupCallBoxLabel),
|
||||
st::boxPadding);
|
||||
if (callback) {
|
||||
box->addButton(std::move(button), callback);
|
||||
if (args.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(); });
|
||||
}
|
||||
|
|
@ -603,7 +607,8 @@ void FillMenu(
|
|||
|
||||
const auto addEditJoinAs = call->showChooseJoinAs();
|
||||
const auto addEditTitle = peer->canManageGroupCall();
|
||||
const auto addEditRecording = peer->canManageGroupCall();
|
||||
const auto addEditRecording = peer->canManageGroupCall()
|
||||
&& !real->scheduleDate();
|
||||
if (addEditJoinAs) {
|
||||
menu->addAction(MakeJoinAsAction(
|
||||
menu->menu(),
|
||||
|
|
@ -660,7 +665,7 @@ void FillMenu(
|
|||
showBox(Box(SettingsBox, strong));
|
||||
}
|
||||
});
|
||||
menu->addAction(MakeFinishAction(menu->menu(), [=] {
|
||||
const auto finish = [=] {
|
||||
if (const auto strong = weak.get()) {
|
||||
showBox(Box(
|
||||
LeaveBox,
|
||||
|
|
@ -668,7 +673,13 @@ void FillMenu(
|
|||
true,
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -9,6 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "base/object_ptr.h"
|
||||
#include "base/unique_qptr.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
|
||||
namespace style {
|
||||
struct FlatLabel;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui {
|
||||
class DropdownMenu;
|
||||
|
|
@ -38,11 +43,19 @@ void LeaveBox(
|
|||
bool discardChecked,
|
||||
BoxContext context);
|
||||
|
||||
void ConfirmBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
const TextWithEntities &text,
|
||||
rpl::producer<QString> button,
|
||||
Fn<void()> callback);
|
||||
struct ConfirmBoxArgs {
|
||||
TextWithEntities text;
|
||||
rpl::producer<QString> button;
|
||||
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(
|
||||
not_null<Ui::DropdownMenu*> menu,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/dropdown_menu.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/chat/group_call_bar.h"
|
||||
#include "ui/layers/layer_manager.h"
|
||||
#include "ui/layers/generic_box.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_session.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "main/main_session.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "boxes/peers/edit_participants_box.h"
|
||||
#include "boxes/peers/add_participants_box.h"
|
||||
#include "boxes/peer_lists_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "base/timer_rpl.h"
|
||||
#include "app.h"
|
||||
#include "apiwrap.h" // api().kickParticipant.
|
||||
#include "styles/style_calls.h"
|
||||
|
|
@ -55,6 +59,7 @@ namespace {
|
|||
constexpr auto kSpacePushToTalkDelay = crl::time(250);
|
||||
constexpr auto kRecordingAnimationDuration = crl::time(1200);
|
||||
constexpr auto kRecordingOpacity = 0.6;
|
||||
constexpr auto kStartNoConfirmation = TimeId(10);
|
||||
|
||||
class InviteController final : public ParticipantsBoxController {
|
||||
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(
|
||||
QWidget *parent,
|
||||
rpl::producer<QString> text) {
|
||||
|
|
@ -181,7 +303,7 @@ void InviteController::itemDeselectedHook(not_null<PeerData*> peer) {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
|
@ -235,7 +357,7 @@ void InviteContactsController::prepareViewHook() {
|
|||
std::move(
|
||||
_discoveredInGroup
|
||||
) | 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);
|
||||
}
|
||||
}, _lifetime);
|
||||
|
|
@ -260,25 +382,30 @@ Panel::Panel(not_null<GroupCall*> call)
|
|||
_window->body(),
|
||||
st::groupCallTitle))
|
||||
#endif // !Q_OS_MAC
|
||||
, _members(widget(), call)
|
||||
, _settings(widget(), st::groupCallSettings)
|
||||
, _mute(std::make_unique<Ui::CallMuteButton>(
|
||||
widget(),
|
||||
Core::App().appDeactivatedValue(),
|
||||
Ui::CallMuteButtonState{
|
||||
.text = tr::lng_group_call_connecting(tr::now),
|
||||
.type = Ui::CallMuteButtonType::Connecting,
|
||||
.text = (_call->scheduleDate()
|
||||
? 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) {
|
||||
_layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox);
|
||||
_settings->setColorOverrides(_mute->colorOverrides());
|
||||
_layerBg->setHideByBackgroundClick(true);
|
||||
|
||||
SubscribeToMigration(
|
||||
_peer,
|
||||
_window->lifetime(),
|
||||
[=](not_null<ChannelData*> channel) { migrate(channel); });
|
||||
setupRealCallViewers(call);
|
||||
setupRealCallViewers();
|
||||
|
||||
initWindow();
|
||||
initWidget();
|
||||
|
|
@ -287,30 +414,7 @@ Panel::Panel(not_null<GroupCall*> call)
|
|||
showAndActivate();
|
||||
setupJoinAsChangedToasts();
|
||||
setupTitleChangedToasts();
|
||||
|
||||
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());
|
||||
setupAllowedToSpeakToasts();
|
||||
}
|
||||
|
||||
Panel::~Panel() {
|
||||
|
|
@ -319,17 +423,8 @@ Panel::~Panel() {
|
|||
}
|
||||
}
|
||||
|
||||
void Panel::setupRealCallViewers(not_null<GroupCall*> call) {
|
||||
const auto peer = call->peer();
|
||||
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
|
||||
void Panel::setupRealCallViewers() {
|
||||
_call->real(
|
||||
) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) {
|
||||
subscribeToChanges(real);
|
||||
}, _window->lifetime());
|
||||
|
|
@ -394,11 +489,9 @@ void Panel::initWindow() {
|
|||
} else if (e->type() == QEvent::KeyPress
|
||||
|| e->type() == QEvent::KeyRelease) {
|
||||
if (static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Space) {
|
||||
if (_call) {
|
||||
_call->pushToTalk(
|
||||
e->type() == QEvent::KeyPress,
|
||||
kSpacePushToTalkDelay);
|
||||
}
|
||||
_call->pushToTalk(
|
||||
e->type() == QEvent::KeyPress,
|
||||
kSpacePushToTalkDelay);
|
||||
}
|
||||
}
|
||||
return base::EventFilterResult::Continue;
|
||||
|
|
@ -440,9 +533,7 @@ void Panel::initWidget() {
|
|||
}
|
||||
|
||||
void Panel::endCall() {
|
||||
if (!_call) {
|
||||
return;
|
||||
} else if (!_call->peer()->canManageGroupCall()) {
|
||||
if (!_call->peer()->canManageGroupCall()) {
|
||||
_call->hangup();
|
||||
return;
|
||||
}
|
||||
|
|
@ -453,11 +544,45 @@ void Panel::endCall() {
|
|||
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() {
|
||||
_mute->clicks(
|
||||
) | rpl::filter([=](Qt::MouseButton button) {
|
||||
return (button == Qt::LeftButton) && (_call != nullptr);
|
||||
return (button == Qt::LeftButton);
|
||||
}) | 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 newState = (oldState == MuteState::ForceMuted)
|
||||
? MuteState::RaisedHand
|
||||
|
|
@ -469,34 +594,47 @@ void Panel::initControls() {
|
|||
_call->setMutedAndUpdate(newState);
|
||||
}, _mute->lifetime());
|
||||
|
||||
initShareAction();
|
||||
refreshLeftButton();
|
||||
|
||||
_hangup->setClickedCallback([=] { endCall(); });
|
||||
_settings->setClickedCallback([=] {
|
||||
if (_call) {
|
||||
_layerBg->showBox(Box(SettingsBox, _call));
|
||||
}
|
||||
});
|
||||
|
||||
_settings->setText(tr::lng_group_call_settings());
|
||||
_hangup->setText(tr::lng_group_call_leave());
|
||||
const auto scheduleDate = _call->scheduleDate();
|
||||
_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(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateControlsGeometry();
|
||||
}, _members->lifetime());
|
||||
setupScheduledLabels(rpl::single(
|
||||
scheduleDate
|
||||
) | rpl::then(rpl::duplicate(changes)));
|
||||
|
||||
initWithCall(_call);
|
||||
}
|
||||
auto started = std::move(changes) | rpl::filter([](TimeId date) {
|
||||
return (date == 0);
|
||||
}) | rpl::take(1);
|
||||
|
||||
void Panel::initWithCall(GroupCall *call) {
|
||||
_callLifetime.destroy();
|
||||
_call = call;
|
||||
if (!_call) {
|
||||
return;
|
||||
rpl::merge(
|
||||
rpl::duplicate(started) | rpl::to_empty,
|
||||
_peer->session().changes().peerFlagsValue(
|
||||
_peer,
|
||||
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) {
|
||||
return (state == State::HangingUp)
|
||||
|| (state == State::Ended)
|
||||
|
|
@ -506,13 +644,214 @@ void Panel::initWithCall(GroupCall *call) {
|
|||
closeBeforeDestroy();
|
||||
}, _callLifetime);
|
||||
|
||||
call->levelUpdates(
|
||||
_call->levelUpdates(
|
||||
) | rpl::filter([=](const LevelUpdate &update) {
|
||||
return update.me;
|
||||
}) | rpl::start_with_next([=](const LevelUpdate &update) {
|
||||
_mute->setLevel(update.value);
|
||||
}, _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(
|
||||
) | rpl::start_with_next([=](MuteRequest request) {
|
||||
if (_call) {
|
||||
|
|
@ -532,73 +871,14 @@ void Panel::initWithCall(GroupCall *call) {
|
|||
kickParticipant(participantPeer);
|
||||
}, _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(
|
||||
) | rpl::start_with_next([=] {
|
||||
if (_call) {
|
||||
if (_peer->isBroadcast() && _peer->asChannel()->hasUsername()) {
|
||||
shareLink();
|
||||
} else {
|
||||
addMembers();
|
||||
}
|
||||
if (_peer->isBroadcast() && _peer->asChannel()->hasUsername()) {
|
||||
_shareLinkCallback();
|
||||
} else {
|
||||
addMembers();
|
||||
}
|
||||
}, _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() {
|
||||
|
|
@ -624,7 +904,7 @@ void Panel::setupJoinAsChangedToasts() {
|
|||
void Panel::setupTitleChangedToasts() {
|
||||
_call->titleChanged(
|
||||
) | rpl::filter([=] {
|
||||
return _peer->groupCall() && _peer->groupCall()->id() == _call->id();
|
||||
return (_call->lookupReal() != nullptr);
|
||||
}) | rpl::map([=] {
|
||||
return _peer->groupCall()->title().isEmpty()
|
||||
? _peer->name
|
||||
|
|
@ -641,9 +921,31 @@ void Panel::setupTitleChangedToasts() {
|
|||
}, widget()->lifetime());
|
||||
}
|
||||
|
||||
void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
||||
_titleText = real->titleValue();
|
||||
void Panel::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 = _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) {
|
||||
if (!recording && _recordingMark) {
|
||||
_recordingMark.destroy();
|
||||
|
|
@ -703,7 +1005,7 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
|||
.parentOverride = widget(),
|
||||
.text = (recorded
|
||||
? tr::lng_group_call_recording_started
|
||||
: (_call && _call->recordingStoppedByMe())
|
||||
: _call->recordingStoppedByMe()
|
||||
? tr::lng_group_call_recording_saved
|
||||
: tr::lng_group_call_recording_stopped)(
|
||||
tr::now,
|
||||
|
|
@ -752,9 +1054,7 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
|||
void Panel::chooseJoinAs() {
|
||||
const auto context = ChooseJoinAsProcess::Context::Switch;
|
||||
const auto callback = [=](JoinInfo info) {
|
||||
if (_call) {
|
||||
_call->rejoinAs(info);
|
||||
}
|
||||
_call->rejoinAs(info);
|
||||
};
|
||||
const auto showBox = [=](object_ptr<Ui::BoxContent> next) {
|
||||
_layerBg->showBox(std::move(next));
|
||||
|
|
@ -775,7 +1075,7 @@ void Panel::chooseJoinAs() {
|
|||
}
|
||||
|
||||
void Panel::showMainMenu() {
|
||||
if (_menu || !_call) {
|
||||
if (_menu) {
|
||||
return;
|
||||
}
|
||||
_menu.create(widget(), st::groupCallDropdownMenu);
|
||||
|
|
@ -822,8 +1122,8 @@ void Panel::showMainMenu() {
|
|||
}
|
||||
|
||||
void Panel::addMembers() {
|
||||
const auto real = _peer->groupCall();
|
||||
if (!_call || !real || real->id() != _call->id()) {
|
||||
const auto real = _call->lookupReal();
|
||||
if (!real) {
|
||||
return;
|
||||
}
|
||||
auto alreadyIn = _peer->owner().invitedToCallUsers(real->id());
|
||||
|
|
@ -849,7 +1149,7 @@ void Panel::addMembers() {
|
|||
&st::groupCallInviteMembersList,
|
||||
&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 call = weak.get();
|
||||
if (!call) {
|
||||
|
|
@ -916,11 +1216,14 @@ void Panel::addMembers() {
|
|||
}
|
||||
finish();
|
||||
};
|
||||
auto box = Box(
|
||||
ConfirmBox,
|
||||
TextWithEntities{ text },
|
||||
tr::lng_participant_invite(),
|
||||
[=] { inviteWithAdd(users, nonMembers, finishWithConfirm); });
|
||||
const auto done = [=] {
|
||||
inviteWithAdd(users, nonMembers, finishWithConfirm);
|
||||
};
|
||||
auto box = ConfirmBox({
|
||||
.text = { text },
|
||||
.button = tr::lng_participant_invite(),
|
||||
.callback = done,
|
||||
});
|
||||
*shared = box.data();
|
||||
_layerBg->showBox(std::move(box));
|
||||
};
|
||||
|
|
@ -1032,7 +1335,7 @@ void Panel::showControls() {
|
|||
|
||||
void Panel::closeBeforeDestroy() {
|
||||
_window->close();
|
||||
initWithCall(nullptr);
|
||||
_callLifetime.destroy();
|
||||
}
|
||||
|
||||
void Panel::initGeometry() {
|
||||
|
|
@ -1064,38 +1367,26 @@ QRect Panel::computeTitleRect() const {
|
|||
}
|
||||
|
||||
void Panel::updateControlsGeometry() {
|
||||
if (widget()->size().isEmpty()) {
|
||||
if (widget()->size().isEmpty() || (!_settings && !_share)) {
|
||||
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 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 fullWidth = muteSize
|
||||
+ 2 * _settings->width()
|
||||
+ 2 * (_settings ? _settings : _share)->width()
|
||||
+ 2 * st::groupCallButtonSkip;
|
||||
_mute->moveInner({ (widget()->width() - muteSize) / 2, muteTop });
|
||||
_settings->moveToLeft((widget()->width() - fullWidth) / 2, buttonsTop);
|
||||
_hangup->moveToRight((widget()->width() - fullWidth) / 2, buttonsTop);
|
||||
const auto leftButtonLeft = (widget()->width() - fullWidth) / 2;
|
||||
if (_settings) {
|
||||
_settings->moveToLeft(leftButtonLeft, buttonsTop);
|
||||
}
|
||||
if (_share) {
|
||||
_share->moveToLeft(leftButtonLeft, buttonsTop);
|
||||
}
|
||||
_hangup->moveToRight(leftButtonLeft, buttonsTop);
|
||||
|
||||
updateMembersGeometry();
|
||||
refreshTitle();
|
||||
|
||||
#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() {
|
||||
if (!_title) {
|
||||
auto text = rpl::combine(
|
||||
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([=](
|
||||
const TextWithEntities &name,
|
||||
const QString &title) {
|
||||
|
|
@ -1144,11 +1467,25 @@ void Panel::refreshTitle() {
|
|||
if (!_subtitle) {
|
||||
_subtitle.create(
|
||||
widget(),
|
||||
tr::lng_group_call_members(
|
||||
lt_count_decimal,
|
||||
_members->fullCountValue() | rpl::map([](int value) {
|
||||
return (value > 0) ? float64(value) : 1.;
|
||||
})),
|
||||
rpl::single(
|
||||
_call->scheduleDate()
|
||||
) | rpl::then(
|
||||
_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);
|
||||
_subtitle->show();
|
||||
_subtitle->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class Window;
|
|||
class ScrollArea;
|
||||
class GenericBox;
|
||||
class LayerManager;
|
||||
class GroupCallScheduledLeft;
|
||||
namespace Platform {
|
||||
class TitleControls;
|
||||
} // namespace Platform
|
||||
|
|
@ -73,16 +74,23 @@ private:
|
|||
void initWindow();
|
||||
void initWidget();
|
||||
void initControls();
|
||||
void initWithCall(GroupCall *call);
|
||||
void initShareAction();
|
||||
void initLayout();
|
||||
void initGeometry();
|
||||
void setupScheduledLabels(rpl::producer<TimeId> date);
|
||||
void setupMembers();
|
||||
void setupJoinAsChangedToasts();
|
||||
void setupTitleChangedToasts();
|
||||
void setupAllowedToSpeakToasts();
|
||||
void setupRealMuteButtonState(not_null<Data::GroupCall*> real);
|
||||
|
||||
bool handleClose();
|
||||
void startScheduledNow();
|
||||
|
||||
void updateControlsGeometry();
|
||||
void updateMembersGeometry();
|
||||
void showControls();
|
||||
void refreshLeftButton();
|
||||
|
||||
void endCall();
|
||||
|
||||
|
|
@ -94,13 +102,13 @@ private:
|
|||
[[nodiscard]] QRect computeTitleRect() const;
|
||||
void refreshTitle();
|
||||
void refreshTitleGeometry();
|
||||
void setupRealCallViewers(not_null<GroupCall*> call);
|
||||
void setupRealCallViewers();
|
||||
void subscribeToChanges(not_null<Data::GroupCall*> real);
|
||||
|
||||
void migrate(not_null<ChannelData*> channel);
|
||||
void subscribeToPeerChanges();
|
||||
|
||||
GroupCall *_call = nullptr;
|
||||
const not_null<GroupCall*> _call;
|
||||
not_null<PeerData*> _peer;
|
||||
|
||||
const std::unique_ptr<Ui::Window> _window;
|
||||
|
|
@ -118,13 +126,18 @@ private:
|
|||
object_ptr<Ui::IconButton> _menuToggle = { nullptr };
|
||||
object_ptr<Ui::DropdownMenu> _menu = { nullptr };
|
||||
object_ptr<Ui::AbstractButton> _joinAsToggle = { nullptr };
|
||||
object_ptr<Members> _members;
|
||||
rpl::variable<QString> _titleText;
|
||||
object_ptr<Members> _members = { nullptr };
|
||||
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;
|
||||
|
||||
object_ptr<Ui::CallButton> _settings;
|
||||
object_ptr<Ui::CallButton> _settings = { nullptr };
|
||||
object_ptr<Ui::CallButton> _share = { nullptr };
|
||||
std::unique_ptr<Ui::CallMuteButton> _mute;
|
||||
object_ptr<Ui::CallButton> _hangup;
|
||||
Fn<void()> _shareLinkCallback;
|
||||
|
||||
rpl::lifetime _peerLifetime;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_group_call.h"
|
||||
#include "data/data_changes.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_media_devices.h"
|
||||
#include "settings/settings_common.h"
|
||||
|
|
@ -149,8 +149,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
|
|||
}
|
||||
text.append(error.first);
|
||||
if (const auto weak = *box) {
|
||||
weak->getDelegate()->show(
|
||||
Box(ConfirmBox, text, nullptr, nullptr));
|
||||
weak->getDelegate()->show(ConfirmBox({ .text = text }));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -677,7 +676,7 @@ std::pair<Fn<void()>, rpl::lifetime> ShareInviteLinkAction(
|
|||
return true;
|
||||
};
|
||||
auto callback = [=] {
|
||||
const auto real = peer->groupCall();
|
||||
const auto real = peer->migrateToOrMe()->groupCall();
|
||||
if (shareReady() || state->generatingLink || !real) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -702,11 +701,11 @@ std::pair<Fn<void()>, rpl::lifetime> ShareInviteLinkAction(
|
|||
state->linkSpeakerRequestId = peer->session().api().request(
|
||||
MTPphone_ExportGroupCallInvite(
|
||||
MTP_flags(Flag::f_can_self_unmute),
|
||||
real->input()
|
||||
)).done([=](const MTPphone_ExportedGroupCallInvite &result) {
|
||||
real->input())
|
||||
).done([=](const MTPphone_ExportedGroupCallInvite &result) {
|
||||
state->linkSpeakerRequestId = 0;
|
||||
result.match([&](
|
||||
const MTPDphone_exportedGroupCallInvite &data) {
|
||||
const MTPDphone_exportedGroupCallInvite &data) {
|
||||
state->linkSpeaker = qs(data.vlink());
|
||||
shareReady();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -374,7 +374,7 @@ void Instance::handleCallUpdate(
|
|||
const MTPPhoneCall &call) {
|
||||
if (call.type() == mtpc_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) {
|
||||
LOG(("API Error: User not loaded for phoneCallRequested."));
|
||||
} else if (user->isSelf()) {
|
||||
|
|
@ -411,6 +411,14 @@ void Instance::handleCallUpdate(
|
|||
void Instance::handleGroupCallUpdate(
|
||||
not_null<Main::Session*> session,
|
||||
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) {
|
||||
return data.vcall().match([](const auto &data) {
|
||||
return data.vid().v;
|
||||
|
|
@ -427,14 +435,6 @@ void Instance::handleGroupCallUpdate(
|
|||
} else {
|
||||
applyGroupCallUpdateChecked(session, update);
|
||||
}
|
||||
|
||||
if (_currentGroupCall
|
||||
&& (&_currentGroupCall->peer()->session() == session)) {
|
||||
update.match([&](const MTPDupdateGroupCall &data) {
|
||||
_currentGroupCall->handlePossibleCreateOrJoinResponse(data);
|
||||
}, [](const auto &) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::applyGroupCallUpdateChecked(
|
||||
|
|
|
|||
|
|
@ -57,8 +57,13 @@ constexpr auto kHideBlobsDuration = crl::time(500);
|
|||
constexpr auto kBlobLevelDuration = crl::time(250);
|
||||
constexpr auto kBlobUpdateInterval = crl::time(100);
|
||||
|
||||
auto BarStateFromMuteState(MuteState state, GroupCall::InstanceState instanceState) {
|
||||
return (instanceState == GroupCall::InstanceState::Disconnected)
|
||||
auto BarStateFromMuteState(
|
||||
MuteState state,
|
||||
GroupCall::InstanceState instanceState,
|
||||
TimeId scheduledDate) {
|
||||
return scheduledDate
|
||||
? BarState::ForceMuted
|
||||
: (instanceState == GroupCall::InstanceState::Disconnected)
|
||||
? BarState::Connecting
|
||||
: (state == MuteState::ForceMuted || state == MuteState::RaisedHand)
|
||||
? BarState::ForceMuted
|
||||
|
|
@ -293,19 +298,27 @@ void TopBar::initControls() {
|
|||
_call
|
||||
? mapToState(_call->muted())
|
||||
: _groupCall->muted(),
|
||||
GroupCall::InstanceState::Connected));
|
||||
GroupCall::InstanceState::Connected,
|
||||
_call ? TimeId(0) : _groupCall->scheduleDate()));
|
||||
using namespace rpl::mappers;
|
||||
auto muted = _call
|
||||
? rpl::combine(
|
||||
_call->mutedValue() | rpl::map(mapToState),
|
||||
rpl::single(GroupCall::InstanceState::Connected)
|
||||
rpl::single(GroupCall::InstanceState::Connected),
|
||||
rpl::single(TimeId(0))
|
||||
) | rpl::type_erased()
|
||||
: rpl::combine(
|
||||
(_groupCall->mutedValue()
|
||||
| MapPushToTalkToActive()
|
||||
| rpl::distinct_until_changed()
|
||||
| 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);
|
||||
std::move(
|
||||
muted
|
||||
|
|
|
|||
|
|
@ -399,7 +399,7 @@ EmojiListWidget::EmojiListWidget(
|
|||
for (auto i = 0; i != kEmojiSectionCount; ++i) {
|
||||
const auto section = static_cast<Section>(i);
|
||||
_counts[i] = (section == Section::Recent)
|
||||
? GetRecentEmoji().size()
|
||||
? int(Core::App().settings().recentEmoji().size())
|
||||
: Ui::Emoji::GetSectionCount(section);
|
||||
}
|
||||
|
||||
|
|
@ -500,17 +500,18 @@ void EmojiListWidget::ensureLoaded(int section) {
|
|||
return;
|
||||
}
|
||||
_emoji[section] = (static_cast<Section>(section) == Section::Recent)
|
||||
? GetRecentEmojiSection()
|
||||
? Core::App().settings().recentEmojiSection()
|
||||
: Ui::Emoji::GetSection(static_cast<Section>(section));
|
||||
_counts[section] = _emoji[section].size();
|
||||
if (static_cast<Section>(section) == Section::Recent) {
|
||||
return;
|
||||
}
|
||||
const auto &variants = Core::App().settings().emojiVariants();
|
||||
for (auto &emoji : _emoji[section]) {
|
||||
if (emoji->hasVariants()) {
|
||||
auto j = cEmojiVariants().constFind(emoji->nonColoredId());
|
||||
if (j != cEmojiVariants().cend()) {
|
||||
emoji = emoji->variant(j.value());
|
||||
const auto j = variants.find(emoji->nonColoredId());
|
||||
if (j != end(variants)) {
|
||||
emoji = emoji->variant(j->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -594,10 +595,13 @@ void EmojiListWidget::mousePressEvent(QMouseEvent *e) {
|
|||
if (_selected >= 0) {
|
||||
auto section = (_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;
|
||||
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();
|
||||
} else {
|
||||
_showPickerTimer.callOnce(500);
|
||||
|
|
@ -617,8 +621,11 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
|||
} else if (_pickerSel >= 0) {
|
||||
auto section = (_pickerSel / MatrixRowShift);
|
||||
auto sel = _pickerSel % MatrixRowShift;
|
||||
if (section < kEmojiSectionCount && sel < _emoji[section].size() && _emoji[section][sel]->hasVariants()) {
|
||||
if (cEmojiVariants().contains(_emoji[section][sel]->nonColoredId())) {
|
||||
if (section < kEmojiSectionCount
|
||||
&& sel < _emoji[section].size()
|
||||
&& _emoji[section][sel]->hasVariants()) {
|
||||
const auto &variants = Core::App().settings().emojiVariants();
|
||||
if (variants.contains(_emoji[section][sel]->nonColoredId())) {
|
||||
_picker->hideAnimated();
|
||||
_pickerSel = -1;
|
||||
}
|
||||
|
|
@ -650,7 +657,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
|||
}
|
||||
|
||||
void EmojiListWidget::selectEmoji(EmojiPtr emoji) {
|
||||
AddRecentEmoji(emoji);
|
||||
Core::App().settings().incrementRecentEmoji(emoji);
|
||||
_chosen.fire_copy(emoji);
|
||||
}
|
||||
|
||||
|
|
@ -698,10 +705,7 @@ QRect EmojiListWidget::emojiRect(int section, int sel) {
|
|||
|
||||
void EmojiListWidget::colorChosen(EmojiPtr emoji) {
|
||||
if (emoji->hasVariants()) {
|
||||
cRefEmojiVariants().insert(
|
||||
emoji->nonColoredId(),
|
||||
emoji->variantIndex(emoji));
|
||||
controller()->session().saveSettingsDelayed();
|
||||
Core::App().settings().saveEmojiVariant(emoji);
|
||||
}
|
||||
if (_pickerSel >= 0) {
|
||||
auto section = (_pickerSel / MatrixRowShift);
|
||||
|
|
@ -790,7 +794,7 @@ void EmojiListWidget::processHideFinished() {
|
|||
|
||||
void EmojiListWidget::refreshRecent() {
|
||||
clearSelection();
|
||||
_emoji[0] = GetRecentEmojiSection();
|
||||
_emoji[0] = Core::App().settings().recentEmojiSection();
|
||||
_counts[0] = _emoji[0].size();
|
||||
resizeToWidth(width());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,11 +116,11 @@ auto SuggestionsWidget::getRowsByQuery() const -> std::vector<Row> {
|
|||
}) | ranges::to_vector;
|
||||
|
||||
auto lastRecent = begin(result);
|
||||
const auto &recent = GetRecentEmoji();
|
||||
const auto &recent = Core::App().settings().recentEmoji();
|
||||
for (const auto &item : recent) {
|
||||
const auto emoji = item.first->original()
|
||||
? item.first->original()
|
||||
: item.first;
|
||||
const auto emoji = item.emoji->original()
|
||||
? item.emoji->original()
|
||||
: item.emoji;
|
||||
const auto it = ranges::find(result, emoji, [](const Row &row) {
|
||||
return row.emoji.get();
|
||||
});
|
||||
|
|
@ -133,12 +133,12 @@ auto SuggestionsWidget::getRowsByQuery() const -> std::vector<Row> {
|
|||
for (auto &item : result) {
|
||||
item.emoji = [&] {
|
||||
const auto result = item.emoji;
|
||||
const auto &variants = cEmojiVariants();
|
||||
const auto &variants = Core::App().settings().emojiVariants();
|
||||
const auto i = result->hasVariants()
|
||||
? variants.constFind(result->nonColoredId())
|
||||
: variants.cend();
|
||||
return (i != variants.cend())
|
||||
? result->variant(i.value())
|
||||
? variants.find(result->nonColoredId())
|
||||
: end(variants);
|
||||
return (i != end(variants))
|
||||
? result->variant(i->second)
|
||||
: result.get();
|
||||
}();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ QString FieldTagMimeProcessor::tagFromMimeTag(const QString &mimeTag) {
|
|||
const auto userId = _controller->session().userId();
|
||||
auto match = QRegularExpression(":(\\d+)$").match(mimeTag);
|
||||
if (!match.hasMatch()
|
||||
|| match.capturedRef(1).toInt() != userId) {
|
||||
|| match.capturedRef(1).toULongLong() != userId.bare) {
|
||||
return QString();
|
||||
}
|
||||
return mimeTag.mid(0, mimeTag.size() - match.capturedLength());
|
||||
|
|
@ -249,7 +249,7 @@ TextWithEntities StripSupportHashtag(TextWithEntities &&text) {
|
|||
|
||||
QString PrepareMentionTag(not_null<UserData*> user) {
|
||||
return TextUtilities::kMentionTagStart
|
||||
+ QString::number(user->bareId())
|
||||
+ QString::number(user->id.value)
|
||||
+ '.'
|
||||
+ QString::number(user->accessHash());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
enum {
|
||||
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
|
||||
LocalEncryptNoPwdIterCount = 4, // key derivation iteration count without pwd (not secure anyway)
|
||||
LocalEncryptSaltSize = 32, // 256 bit
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "storage/storage_domain.h"
|
||||
#include "storage/storage_databases.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "payments/payments_checkout_process.h"
|
||||
#include "export/export_manager.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/window_controller.h"
|
||||
|
|
@ -169,6 +170,15 @@ Application::~Application() {
|
|||
_window = nullptr;
|
||||
_mediaView = nullptr;
|
||||
_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();
|
||||
|
||||
Local::finish();
|
||||
|
|
@ -550,6 +560,9 @@ void Application::badMtprotoConfigurationError() {
|
|||
void Application::startLocalStorage() {
|
||||
Local::start();
|
||||
_saveSettingsTimer.emplace([=] { saveSettings(); });
|
||||
_settings.saveDelayedRequests() | rpl::start_with_next([=] {
|
||||
saveSettingsDelayed();
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
void Application::startEmojiImageLoader() {
|
||||
|
|
|
|||
|
|
@ -21,17 +21,20 @@ void BaseIntegration::enterFromEventLoop(FnMut<void()> &&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) {
|
||||
#ifdef DEBUG_LOG
|
||||
DEBUG_LOG((message));
|
||||
#endif // DEBUG_LOG
|
||||
Logs::writeMain(message);
|
||||
}
|
||||
|
||||
void BaseIntegration::logAssertionViolation(const QString &info) {
|
||||
#ifdef LOG
|
||||
LOG(("Assertion Failed! ") + info);
|
||||
#endif // LOG
|
||||
|
||||
Logs::writeMain("Assertion Failed! " + info);
|
||||
CrashReports::SetAnnotation("Assertion", info);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
namespace Core {
|
||||
|
||||
class BaseIntegration : public base::Integration {
|
||||
class BaseIntegration final : public base::Integration {
|
||||
public:
|
||||
BaseIntegration(int argc, char *argv[]);
|
||||
|
||||
void enterFromEventLoop(FnMut<void()> &&method) override;
|
||||
bool logSkipDebug() override;
|
||||
void logMessageDebug(const QString &message) override;
|
||||
void logMessage(const QString &message) override;
|
||||
void logAssertionViolation(const QString &info) override;
|
||||
[[nodiscard]] bool gtkIntegrationEnabled() const override;
|
||||
|
|
|
|||
|
|
@ -87,12 +87,12 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const {
|
|||
open();
|
||||
} else if (!_bot
|
||||
|| _bot->isVerified()
|
||||
|| _bot->session().local().isBotTrusted(_bot)) {
|
||||
|| _bot->session().local().isBotTrustedOpenGame(_bot->id)) {
|
||||
open();
|
||||
} else {
|
||||
const auto callback = [=, bot = _bot] {
|
||||
Ui::hideLayer();
|
||||
bot->session().local().markBotTrusted(bot);
|
||||
bot->session().local().markBotTrustedOpenGame(bot->id);
|
||||
open();
|
||||
};
|
||||
Ui::show(Box<ConfirmBox>(
|
||||
|
|
@ -137,7 +137,9 @@ void MentionNameClickHandler::onClick(ClickContext context) const {
|
|||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Core {
|
||||
namespace {
|
||||
|
||||
constexpr auto kRecentEmojiLimit = 42;
|
||||
|
||||
[[nodiscard]] WindowPosition Deserialize(const QByteArray &data) {
|
||||
QDataStream stream(data);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
|
|
@ -75,6 +77,17 @@ QByteArray Settings::serialize() const {
|
|||
const auto themesAccentColors = _themesAccentColors.serialize();
|
||||
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)
|
||||
+ sizeof(qint32) * 5
|
||||
+ Serialize::stringSize(_downloadPath.current())
|
||||
|
|
@ -83,10 +96,16 @@ QByteArray Settings::serialize() const {
|
|||
+ Serialize::stringSize(_callOutputDeviceId)
|
||||
+ Serialize::stringSize(_callInputDeviceId)
|
||||
+ Serialize::stringSize(_callVideoInputDeviceId)
|
||||
+ sizeof(qint32) * 3;
|
||||
+ sizeof(qint32) * 5;
|
||||
for (const auto &[key, value] : _soundOverrides) {
|
||||
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(windowPosition);
|
||||
|
||||
|
|
@ -165,7 +184,16 @@ QByteArray Settings::serialize() const {
|
|||
<< qint64(_groupCallPushToTalkDelay)
|
||||
<< qint32(0) // Call audio backend
|
||||
<< 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;
|
||||
}
|
||||
|
|
@ -239,6 +267,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
|||
qint32 callAudioBackend = 0;
|
||||
qint32 disableCalls = _disableCalls ? 1 : 0;
|
||||
QByteArray windowPosition;
|
||||
std::vector<RecentEmojiId> recentEmojiPreload;
|
||||
base::flat_map<QString, uint8> emojiVariants;
|
||||
|
||||
stream >> themesAccentColors;
|
||||
if (!stream.atEnd()) {
|
||||
|
|
@ -343,6 +373,30 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
|||
if (!stream.atEnd()) {
|
||||
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) {
|
||||
LOG(("App Error: "
|
||||
"Bad data for Core::Settings::constructFromSerialized()"));
|
||||
|
|
@ -446,6 +500,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
|||
if (!windowPosition.isEmpty()) {
|
||||
_windowPosition = Deserialize(windowPosition);
|
||||
}
|
||||
_recentEmojiPreload = std::move(recentEmojiPreload);
|
||||
_emojiVariants = std::move(emojiVariants);
|
||||
}
|
||||
|
||||
bool Settings::chatWide() const {
|
||||
|
|
@ -525,6 +581,118 @@ rpl::producer<int> Settings::thirdColumnWidthChanges() const {
|
|||
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() {
|
||||
_adaptiveForWide = true;
|
||||
_moderateModeEnabled = false;
|
||||
|
|
@ -592,6 +760,10 @@ void Settings::resetOnLastLogout() {
|
|||
_notifyFromAll = true;
|
||||
_tabbedReplacedWithInfo = false; // per-window
|
||||
_systemDarkModeEnabled = false;
|
||||
|
||||
_recentEmojiPreload.clear();
|
||||
_recentEmoji.clear();
|
||||
_emojiVariants.clear();
|
||||
}
|
||||
|
||||
bool Settings::ThirdColumnByDefault() {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "window/themes/window_themes_embedded.h"
|
||||
#include "ui/chat/attach/attach_send_files_way.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
#include "emoji.h"
|
||||
|
||||
enum class RectPart;
|
||||
|
||||
|
|
@ -53,6 +54,10 @@ public:
|
|||
|
||||
Settings();
|
||||
|
||||
[[nodiscard]] rpl::producer<> saveDelayedRequests() const {
|
||||
return _saveDelayed.events();
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool IsLeftCorner(ScreenCorner corner) {
|
||||
return (corner == ScreenCorner::TopLeft)
|
||||
|| (corner == ScreenCorner::BottomLeft);
|
||||
|
|
@ -510,8 +515,26 @@ public:
|
|||
_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]] float64 DefaultDialogsWidthRatio();
|
||||
[[nodiscard]] static float64 DefaultDialogsWidthRatio();
|
||||
[[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) {
|
||||
return int(std::round(std::clamp(speed, 0.5, 2.0) * 100));
|
||||
}
|
||||
|
|
@ -527,10 +550,17 @@ public:
|
|||
void resetOnLastLogout();
|
||||
|
||||
private:
|
||||
void resolveRecentEmoji() const;
|
||||
|
||||
static constexpr auto kDefaultThirdColumnWidth = 0;
|
||||
static constexpr auto kDefaultDialogsWidthRatio = 5. / 14;
|
||||
static constexpr auto kDefaultBigDialogsWidthRatio = 0.275;
|
||||
|
||||
struct RecentEmojiId {
|
||||
QString emoji;
|
||||
ushort rating = 0;
|
||||
};
|
||||
|
||||
bool _adaptiveForWide = true;
|
||||
bool _moderateModeEnabled = false;
|
||||
rpl::variable<float64> _songVolume = kDefaultVolume;
|
||||
|
|
@ -578,6 +608,10 @@ private:
|
|||
rpl::variable<std::vector<int>> _dictionariesEnabled;
|
||||
rpl::variable<bool> _autoDownloadDictionaries = 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
|
||||
Window::Column _floatPlayerColumn = Window::Column(); // per-window
|
||||
RectPart _floatPlayerCorner = RectPart(); // per-window
|
||||
|
|
@ -595,6 +629,7 @@ private:
|
|||
bool _tabbedReplacedWithInfo = false; // per-window
|
||||
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window
|
||||
|
||||
rpl::event_stream<> _saveDelayed;
|
||||
float64 _rememberedSongVolume = kDefaultVolume;
|
||||
bool _rememberedSoundNotifyFromTray = false;
|
||||
bool _rememberedFlashBounceNotifyFromTray = false;
|
||||
|
|
|
|||
|
|
@ -283,19 +283,7 @@ void Launcher::init() {
|
|||
_arguments = readArguments(_argc, _argv);
|
||||
|
||||
prepareSettings();
|
||||
|
||||
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));
|
||||
}
|
||||
});
|
||||
initQtMessageLogging();
|
||||
|
||||
QApplication::setApplicationName(qsl("KotatogramDesktop"));
|
||||
|
||||
|
|
@ -451,6 +439,26 @@ void Launcher::prepareSettings() {
|
|||
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 {
|
||||
return InstallationTag;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ protected:
|
|||
|
||||
private:
|
||||
void prepareSettings();
|
||||
void initQtMessageLogging();
|
||||
void processArguments();
|
||||
|
||||
QStringList readArguments(int argc, char *argv[]) const;
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ bool ShowPassportForm(
|
|||
if (!controller) {
|
||||
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 callback = params.value("callback_url", QString());
|
||||
const auto publicKey = params.value("public_key", QString());
|
||||
|
|
@ -323,7 +323,8 @@ bool ResolvePrivatePost(
|
|||
const auto params = url_parse_params(
|
||||
match->captured(1),
|
||||
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 commentParam = params.value(qsl("comment"));
|
||||
const auto commentId = commentParam.toInt();
|
||||
|
|
|
|||