Updated TDesktop sources to 2.7.2

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

3
.gitattributes vendored
View file

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

2
.gitignore vendored
View file

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

3
.gitmodules vendored
View file

@ -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

View file

@ -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

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 B

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 B

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 B

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 949 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -216,6 +216,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_code_desc" = "We have sent you a message with activation\ncode to your phone. Please enter it below.";
"lng_code_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 havent 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}**";

View file

@ -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>

View file

@ -68,7 +68,7 @@ inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string pro
inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia;
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

View file

@ -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>

View file

@ -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"

View file

@ -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"

View file

@ -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(),

View file

@ -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);
}

View file

@ -37,15 +37,17 @@ EntitiesInText EntitiesFromMTP(
case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break;
case mtpc_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));

View file

@ -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);

View file

@ -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)) {

View file

@ -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 <

View file

@ -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();

View file

@ -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)

View file

@ -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());

View file

@ -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);

View file

@ -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,

View file

@ -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:

View file

@ -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;

View file

@ -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)) {

View file

@ -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;

View file

@ -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,

View file

@ -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));

View file

@ -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;
}

View file

@ -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);

View file

@ -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));

View file

@ -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;
}

View file

@ -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);

View file

@ -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));
}
}

View file

@ -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"

View file

@ -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);

View file

@ -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) {

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

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

View file

@ -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,

View file

@ -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 };

View file

@ -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(

View file

@ -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,

View file

@ -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);

View file

@ -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;

View file

@ -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();
});

View file

@ -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(

View file

@ -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

View file

@ -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());
}

View file

@ -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();
}();
}

View file

@ -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());
}

View file

@ -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

View file

@ -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() {

View file

@ -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);
}

View file

@ -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;

View file

@ -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 };
}

View file

@ -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() {

View file

@ -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;

View file

@ -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;
}

View file

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

View file

@ -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();

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