Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0f54822e0 | ||
|
|
df4c9a0765 | ||
|
|
47e15d136d | ||
|
|
5aff4cca0e | ||
|
|
a064e836f2 | ||
|
|
81ed3acfa1 | ||
|
|
cc2dd31555 | ||
|
|
0ee6395022 | ||
|
|
1d2a18929a | ||
|
|
a0a3de125a | ||
|
|
1cf207671e | ||
|
|
c208b5dc58 | ||
|
|
6eb5d47c35 | ||
|
|
59977da2b4 | ||
|
|
4550b2a315 | ||
|
|
bcbab7ad8e | ||
|
|
b635a9d4a5 | ||
|
|
eadd7704ef | ||
|
|
e6f0c176f7 | ||
|
|
22d4331ead | ||
|
|
1247fde04e | ||
|
|
88a2f05c6d | ||
|
|
df15c67dab | ||
|
|
f59db10267 | ||
|
|
d29a1f5cd2 | ||
|
|
cd8d257c70 | ||
|
|
9dfc60026e | ||
|
|
e9a5c45f34 | ||
|
|
2c07bdd0e8 | ||
|
|
b106438de8 | ||
|
|
92b7afc5f5 | ||
|
|
0f28069881 | ||
|
|
cbe38af427 | ||
|
|
4598ce2671 | ||
|
|
244ccba871 | ||
|
|
dc8eb79295 | ||
|
|
f7abd85761 | ||
|
|
436212bb88 | ||
|
|
8c17e3e578 | ||
|
|
0852dbc40f | ||
|
|
ccea6ce492 | ||
|
|
2d0bcf7dca | ||
|
|
ae40ea9336 | ||
|
|
52a6282eb9 | ||
|
|
c39024c7fd | ||
|
|
85e4c8527b | ||
|
|
f2da34c9f5 | ||
|
|
9709297713 | ||
|
|
809b0e9fe0 | ||
|
|
d684c8057c | ||
|
|
d5820e7a5a | ||
|
|
beff635e45 | ||
|
|
5de83ef30c | ||
|
|
f5a323e40a | ||
|
|
0a1e84ddb2 | ||
|
|
3cd0f9d189 | ||
|
|
f3dd8c68b3 | ||
|
|
70808dfa7d | ||
|
|
0821c21285 | ||
|
|
29c0956d61 | ||
|
|
0cfede984c | ||
|
|
558e1d96fd | ||
|
|
44c188024e | ||
|
|
3acbcc6247 | ||
|
|
6caddb5141 | ||
|
|
963fda69a8 | ||
|
|
6ff0cb853d | ||
|
|
10c8162575 | ||
|
|
e4640590d0 | ||
|
|
e5b85bbaf1 | ||
|
|
8310d7e47c | ||
|
|
cf523953ad | ||
|
|
3e80c04da7 | ||
|
|
c593f43629 | ||
|
|
86aaa9673d | ||
|
|
a230e83778 | ||
|
|
df02bbb0a3 | ||
|
|
1b3075ac2e | ||
|
|
d45c530db0 | ||
|
|
2671e67119 | ||
|
|
bdc275a927 | ||
|
|
6192413f0b | ||
|
|
51df482571 | ||
|
|
2694cb76a7 | ||
|
|
b5ae492f5e | ||
|
|
018ee0564f | ||
|
|
156eb69d38 | ||
|
|
df0229cffd | ||
|
|
b3f8d0e81a | ||
|
|
559d488b0b | ||
|
|
401529e7d1 | ||
|
|
a6fb0e372e | ||
|
|
7948fc509e | ||
|
|
2d6155fc85 | ||
|
|
c7e60ef723 | ||
|
|
8f5830d520 | ||
|
|
f21d7821e7 | ||
|
|
e8f1373edc | ||
|
|
c8d1e01159 | ||
|
|
7e6f24552a | ||
|
|
27d58ba07b | ||
|
|
3a92a181a1 | ||
|
|
ddd5617043 | ||
|
|
70b3e414ce |
@@ -345,6 +345,8 @@ PRIVATE
|
||||
core/utils.cpp
|
||||
core/utils.h
|
||||
core/version.h
|
||||
countries/countries_manager.cpp
|
||||
countries/countries_manager.h
|
||||
data/stickers/data_stickers_set.cpp
|
||||
data/stickers/data_stickers_set.h
|
||||
data/stickers/data_stickers.cpp
|
||||
@@ -352,6 +354,8 @@ PRIVATE
|
||||
data/data_abstract_sparse_ids.h
|
||||
data/data_abstract_structure.cpp
|
||||
data/data_abstract_structure.h
|
||||
data/data_audio_msg_id.cpp
|
||||
data/data_audio_msg_id.h
|
||||
data/data_auto_download.cpp
|
||||
data/data_auto_download.h
|
||||
data/data_chat.cpp
|
||||
@@ -420,6 +424,8 @@ PRIVATE
|
||||
data/data_reply_preview.h
|
||||
data/data_search_controller.cpp
|
||||
data/data_search_controller.h
|
||||
data/data_send_action.cpp
|
||||
data/data_send_action.h
|
||||
data/data_session.cpp
|
||||
data/data_session.h
|
||||
data/data_scheduled_messages.cpp
|
||||
@@ -854,14 +860,9 @@ PRIVATE
|
||||
payments/payments_form.h
|
||||
platform/linux/linux_desktop_environment.cpp
|
||||
platform/linux/linux_desktop_environment.h
|
||||
platform/linux/linux_gdk_helper.cpp
|
||||
platform/linux/linux_gdk_helper.h
|
||||
platform/linux/linux_gtk_integration_dummy.cpp
|
||||
platform/linux/linux_gtk_integration_p.h
|
||||
platform/linux/linux_gtk_integration.cpp
|
||||
platform/linux/linux_gtk_integration.h
|
||||
platform/linux/linux_gtk_open_with_dialog.cpp
|
||||
platform/linux/linux_gtk_open_with_dialog.h
|
||||
platform/linux/linux_wayland_integration_dummy.cpp
|
||||
platform/linux/linux_wayland_integration.cpp
|
||||
platform/linux/linux_wayland_integration.h
|
||||
@@ -1166,12 +1167,7 @@ endif()
|
||||
|
||||
if (DESKTOP_APP_DISABLE_GTK_INTEGRATION)
|
||||
remove_target_sources(Telegram ${src_loc}
|
||||
platform/linux/linux_gdk_helper.cpp
|
||||
platform/linux/linux_gdk_helper.h
|
||||
platform/linux/linux_gtk_integration_p.h
|
||||
platform/linux/linux_gtk_integration.cpp
|
||||
platform/linux/linux_gtk_open_with_dialog.cpp
|
||||
platform/linux/linux_gtk_open_with_dialog.h
|
||||
)
|
||||
else()
|
||||
remove_target_sources(Telegram ${src_loc}
|
||||
@@ -1288,17 +1284,6 @@ else()
|
||||
desktop-app::external_kwayland
|
||||
)
|
||||
endif()
|
||||
|
||||
if (NOT DESKTOP_APP_DISABLE_GTK_INTEGRATION)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
pkg_check_modules(GTK REQUIRED gtk+-3.0)
|
||||
target_include_directories(Telegram SYSTEM PRIVATE ${GTK_INCLUDE_DIRS})
|
||||
|
||||
if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
|
||||
target_link_libraries(Telegram PRIVATE X11)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (build_macstore)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 103 KiB |
BIN
Telegram/Resources/art/background.tgv
Normal file
BIN
Telegram/Resources/art/background.tgv
Normal file
Binary file not shown.
BIN
Telegram/Resources/art/bg_thumbnail.png
Normal file
BIN
Telegram/Resources/art/bg_thumbnail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 67 KiB |
@@ -1141,10 +1141,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_invite_users_and_one" = "{accumulated}, {user}";
|
||||
"lng_action_invite_users_and_last" = "{accumulated} and {user}";
|
||||
"lng_action_group_call_started_group" = "{from} started a voice chat";
|
||||
"lng_action_group_call_started_channel" = "Voice chat started";
|
||||
"lng_action_group_call_started_channel" = "Live stream started";
|
||||
"lng_action_group_call_scheduled_group" = "{from} scheduled a voice chat for {date}";
|
||||
"lng_action_group_call_scheduled_channel" = "Voice chat scheduled for {date}";
|
||||
"lng_action_group_call_finished" = "Voice chat finished ({duration})";
|
||||
"lng_action_group_call_scheduled_channel" = "Live stream scheduled for {date}";
|
||||
"lng_action_group_call_finished" = "Live stream finished ({duration})";
|
||||
"lng_action_group_call_finished_group" = "{from} ended the voice chat ({duration})";
|
||||
"lng_action_add_user" = "{from} added {user}";
|
||||
"lng_action_add_users_many" = "{from} added {users}";
|
||||
@@ -1209,6 +1209,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_proximity_reached" = "{from} is now within {distance} from {user}";
|
||||
"lng_action_proximity_reached_you" = "{from} is now within {distance} from you";
|
||||
"lng_action_you_proximity_reached" = "You are now within {distance} from {user}";
|
||||
"lng_action_you_theme_changed" = "You changed chat theme to {emoji}";
|
||||
"lng_action_theme_changed" = "{from} changed chat theme to {emoji}";
|
||||
"lng_action_theme_changed_channel" = "Channel theme changed to {emoji}";
|
||||
"lng_action_you_theme_disabled" = "You disabled chat theme";
|
||||
"lng_action_theme_disabled" = "{from} disabled chat theme";
|
||||
"lng_action_theme_disabled_channel" = "Channel theme disabled";
|
||||
"lng_action_proximity_distance_m#one" = "{count} meter";
|
||||
"lng_action_proximity_distance_m#other" = "{count} metres";
|
||||
"lng_action_proximity_distance_km#one" = "{count} km";
|
||||
@@ -1609,6 +1615,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_user_action_upload_photo" = "{user} is sending a photo";
|
||||
"lng_send_action_upload_file" = "sending a file";
|
||||
"lng_user_action_upload_file" = "{user} is sending a file";
|
||||
"lng_send_action_choose_sticker" = "choosing a sticker";
|
||||
"lng_user_action_choose_sticker" = "{user} is choosing a sticker";
|
||||
"lng_unread_bar#one" = "{count} unread message";
|
||||
"lng_unread_bar#other" = "{count} unread messages";
|
||||
"lng_unread_bar_some" = "Unread messages";
|
||||
@@ -1996,6 +2004,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_call_bar_hangup" = "End call";
|
||||
"lng_call_leave_to_other_sure" = "Do you want to end your active call and join a voice chat in this group?";
|
||||
"lng_call_leave_to_other_sure_channel" = "Do you want to end your active call and join a live stream in this channel?";
|
||||
|
||||
"lng_call_box_title" = "Calls";
|
||||
"lng_call_box_about" = "You haven't made any Telegram calls yet.";
|
||||
@@ -2037,6 +2046,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_call_microphone_off" = "{user}'s microphone is off";
|
||||
|
||||
"lng_group_call_title" = "Voice Chat";
|
||||
"lng_group_call_title_channel" = "Live Stream";
|
||||
"lng_group_call_active" = "speaking";
|
||||
"lng_group_call_inactive" = "listening";
|
||||
"lng_group_call_raised_hand_status" = "wants to speak";
|
||||
@@ -2060,13 +2070,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_connecting" = "Connecting...";
|
||||
"lng_group_call_leave" = "Leave";
|
||||
"lng_group_call_leave_title" = "Leave voice chat";
|
||||
"lng_group_call_leave_title_channel" = "Leave live stream";
|
||||
"lng_group_call_leave_sure" = "Are you sure you want to leave this voice chat?";
|
||||
"lng_group_call_leave_sure_channel" = "Are you sure you want to leave this live stream?";
|
||||
"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_close_sure_channel" = "Live stream is scheduled. You can abort it or just close this panel.";
|
||||
"lng_group_call_also_cancel" = "Abort voice chat";
|
||||
"lng_group_call_also_cancel_channel" = "Abort live stream";
|
||||
"lng_group_call_leave_to_other_sure" = "Do you want to leave your active voice chat and join a voice chat in this group?";
|
||||
"lng_group_call_leave_to_other_sure_channel" = "Do you want to leave your active voice chat and join a live stream in this channel?";
|
||||
"lng_group_call_leave_channel_to_other_sure" = "Do you want to leave your active live stream and join a voice chat in this group?";
|
||||
"lng_group_call_leave_channel_to_other_sure_channel" = "Do you want to leave your active live stream and join a live stream in this channel?";
|
||||
"lng_group_call_create_sure" = "Do you really want to start a voice chat in this group?";
|
||||
"lng_group_call_create_sure_channel" = "Are you sure you want to start a voice chat in this channel as your personal account?";
|
||||
"lng_group_call_create_sure_channel" = "Are you sure you want to start a live stream in this channel as your personal account?";
|
||||
"lng_group_call_join_sure_personal" = "Are you sure you want to join this voice chat as your personal account?";
|
||||
"lng_group_call_muted_no_camera" = "You can't turn on video while you're muted by admin.";
|
||||
"lng_group_call_muted_no_screen" = "You can't share your screen while you're muted by admin.";
|
||||
@@ -2081,6 +2098,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_tooltip_force_muted" = "Muted by admin. Click if you want to speak.";
|
||||
"lng_group_call_tooltip_raised_hand" = "You asked to speak. We let the speakers know.";
|
||||
"lng_group_call_also_end" = "End voice chat";
|
||||
"lng_group_call_also_end_channel" = "End live stream";
|
||||
"lng_group_call_settings_title" = "Settings";
|
||||
"lng_group_call_invite" = "Invite Member";
|
||||
"lng_group_call_invited_status" = "invited";
|
||||
@@ -2103,26 +2121,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_ptt_delay" = "Push to Talk release delay: {delay}";
|
||||
"lng_group_call_share" = "Share Invite Link";
|
||||
"lng_group_call_noise_suppression" = "Enable Noise Suppression";
|
||||
"lng_group_call_limit#one" = "Video is only available\nfor the first {count} member";
|
||||
"lng_group_call_limit#other" = "Video is only available\nfor the first {count} members";
|
||||
"lng_group_call_over_limit#one" = "The voice chat is over {count} member.\nNew participants only have access to audio stream.";
|
||||
"lng_group_call_over_limit#other" = "The voice chat is over {count} members.\nNew participants only have access to audio stream.";
|
||||
"lng_group_call_video_paused" = "Video is paused";
|
||||
"lng_group_call_share_speaker" = "Users with this link can speak";
|
||||
"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_end_channel" = "End Live Stream";
|
||||
"lng_group_call_cancel" = "Abort Voice Chat";
|
||||
"lng_group_call_cancel_channel" = "Abort Live Stream";
|
||||
"lng_group_call_join" = "Join";
|
||||
"lng_group_call_join_confirm" = "Do you want to join the voice chat {chat}?";
|
||||
"lng_group_call_join_confirm_channel" = "Do you want to join the live stream {chat}?";
|
||||
"lng_group_call_invite_done_user" = "You invited {user} to the voice chat.";
|
||||
"lng_group_call_invite_done_many#one" = "You invited **{count} member** to the voice chat.";
|
||||
"lng_group_call_invite_done_many#other" = "You invited **{count} members** to the voice chat.";
|
||||
"lng_group_call_no_members" = "click to join";
|
||||
"lng_group_call_members#one" = "{count} participant";
|
||||
"lng_group_call_members#other" = "{count} participants";
|
||||
"lng_group_call_no_anonymous" = "Sorry, anonymous group admins can't join voice chats.";
|
||||
"lng_group_call_too_many" = "Sorry, there are too many members in this voice chat. Please try again later.";
|
||||
"lng_group_call_context_mute" = "Mute";
|
||||
"lng_group_call_context_unmute" = "Allow to speak";
|
||||
"lng_group_call_context_remove_hand" = "Cancel request to speak";
|
||||
@@ -2134,6 +2149,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_context_unpin_screen" = "Unpin screencast";
|
||||
"lng_group_call_context_remove" = "Remove";
|
||||
"lng_group_call_remove_channel" = "Remove {channel} from the voice chat?";
|
||||
"lng_group_call_remove_channel_from_channel" = "Remove {channel} from the live stream?";
|
||||
"lng_group_call_duration_days#one" = "{count} day";
|
||||
"lng_group_call_duration_days#other" = "{count} days";
|
||||
"lng_group_call_duration_hours#one" = "{count} hour";
|
||||
@@ -2150,18 +2166,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_mac_settings" = "Open Settings";
|
||||
|
||||
"lng_group_call_start_as_header" = "Start Voice Chat as...";
|
||||
"lng_group_call_start_as_header_channel" = "Start Live Stream as...";
|
||||
"lng_group_call_join_as_header" = "Join Voice Chat as...";
|
||||
"lng_group_call_join_as_header_channel" = "Join Live Stream 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_channel" = "schedule a live stream";
|
||||
"lng_group_call_schedule_title" = "Schedule Voice Chat";
|
||||
"lng_group_call_schedule_title_channel" = "Schedule Live Stream";
|
||||
"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_schedule_notified_channel" = "The subscribers of the channel will be notified that the live stream will start in {duration}.";
|
||||
"lng_group_call_scheduled_status" = "Scheduled";
|
||||
"lng_group_call_scheduled_title" = "Scheduled Voice Chat";
|
||||
"lng_group_call_scheduled_title_channel" = "Scheduled Live Stream";
|
||||
"lng_group_call_starts_short" = "Starts {when}";
|
||||
"lng_group_call_starts" = "Voice Chat starts {when}";
|
||||
"lng_group_call_starts_channel" = "Live Stream 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}";
|
||||
@@ -2172,16 +2194,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"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_start_now_sure_channel" = "Are you sure you want to start the live stream 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}**";
|
||||
"lng_group_call_edit_title_header" = "Voice chat title";
|
||||
"lng_group_call_edit_title_channel" = "Edit live stream title";
|
||||
"lng_group_call_recording_start" = "Start recording";
|
||||
"lng_group_call_recording_stop" = "Stop recording";
|
||||
"lng_group_call_recording_started" = "Voice chat recording started.";
|
||||
"lng_group_call_recording_started_channel" = "Live stream recording started.";
|
||||
"lng_group_call_recording_stopped" = "Voice chat recording stopped.";
|
||||
"lng_group_call_recording_stopped_channel" = "Live stream recording stopped.";
|
||||
"lng_group_call_recording_saved" = "Audio saved to Saved Messages.";
|
||||
"lng_group_call_pinned_camera_me" = "Your video is pinned.";
|
||||
"lng_group_call_pinned_screen_me" = "Your screencast is pinned.";
|
||||
@@ -2197,18 +2221,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_recording_start_field" = "Recording Title";
|
||||
"lng_group_call_recording_start_button" = "Start";
|
||||
"lng_group_call_is_recorded" = "Voice chat is being recorded.";
|
||||
"lng_group_call_is_recorded_channel" = "Live stream is being recorded.";
|
||||
"lng_group_call_can_speak_here" = "You can now speak.";
|
||||
"lng_group_call_can_speak" = "You can now speak in {chat}.";
|
||||
"lng_group_call_title_changed" = "Voice chat title changed to {title}";
|
||||
"lng_group_call_title_changed_channel" = "Live stream title changed to {title}";
|
||||
"lng_group_call_join_as_changed" = "Members of this voice chat will now see you as {name}";
|
||||
"lng_group_call_join_as_changed_channel" = "Members of this live stream will now see you as {name}";
|
||||
|
||||
"lng_no_mic_permission" = "Telegram needs access to your microphone so that you can make calls and record voice messages.";
|
||||
|
||||
"lng_player_message_today" = "Today at {time}";
|
||||
"lng_player_message_yesterday" = "Yesterday at {time}";
|
||||
"lng_player_message_date" = "{date} at {time}";
|
||||
//"lng_player_cant_stream" = "This file can't be played before it is fully downloaded.\n\nWould you like to download it?";
|
||||
//"lng_player_download" = "Download";
|
||||
|
||||
"lng_rights_edit_admin" = "Manage permissions";
|
||||
"lng_rights_edit_admin_header" = "What can this admin do?";
|
||||
@@ -2264,6 +2289,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_rights_channel_post" = "Post messages";
|
||||
"lng_rights_channel_edit" = "Edit messages of others";
|
||||
"lng_rights_channel_delete" = "Delete messages of others";
|
||||
"lng_rights_channel_manage_calls" = "Manage live streams";
|
||||
"lng_rights_group_info" = "Change group info";
|
||||
"lng_rights_group_ban" = "Ban users";
|
||||
"lng_rights_group_invite_link" = "Invite users via link";
|
||||
@@ -2353,6 +2379,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_filter_messages_edited" = "Edited messages";
|
||||
"lng_admin_log_filter_messages_pinned" = "Pinned messages";
|
||||
"lng_admin_log_filter_voice_chats" = "Voice chat";
|
||||
"lng_admin_log_filter_voice_chats_channel" = "Live stream";
|
||||
"lng_admin_log_filter_invite_links" = "Invite links";
|
||||
"lng_admin_log_filter_members_removed" = "Leaving members";
|
||||
"lng_admin_log_filter_all_admins" = "All users and admins";
|
||||
@@ -2425,12 +2452,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_changed_slow_mode" = "{from} changed slow mode to {duration}";
|
||||
"lng_admin_log_removed_slow_mode" = "{from} disabled slow mode";
|
||||
"lng_admin_log_started_group_call" = "{from} started a new voice chat";
|
||||
"lng_admin_log_started_group_call_channel" = "{from} started a new live stream";
|
||||
"lng_admin_log_discarded_group_call" = "{from} discarded a voice chat";
|
||||
"lng_admin_log_discarded_group_call_channel" = "{from} discarded a live stream";
|
||||
"lng_admin_log_muted_participant" = "{from} muted {user} in a voice chat";
|
||||
"lng_admin_log_muted_participant_channel" = "{from} muted {user} in a live stream";
|
||||
"lng_admin_log_unmuted_participant" = "{from} unmuted {user} in a voice chat";
|
||||
"lng_admin_log_unmuted_participant_channel" = "{from} unmuted {user} in a live stream";
|
||||
"lng_admin_log_allowed_unmute_self" = "{from} allowed new voice chat members to speak";
|
||||
"lng_admin_log_allowed_unmute_self_channel" = "{from} allowed new live stream members to speak";
|
||||
"lng_admin_log_disallowed_unmute_self" = "{from} started muting new voice chat members";
|
||||
"lng_admin_log_disallowed_unmute_self_channel" = "{from} started muting new live stream members";
|
||||
"lng_admin_log_participant_volume" = "{from} changed voice chat volume for {user} to {percent}";
|
||||
"lng_admin_log_participant_volume_channel" = "{from} changed live stream volume for {user} to {percent}";
|
||||
"lng_admin_log_user_with_username" = "{name} ({mention})";
|
||||
"lng_admin_log_messages_ttl_set" = "{from} enabled messages auto-delete after {duration}";
|
||||
"lng_admin_log_messages_ttl_changed" = "{from} changed messages auto-delete period from {previous} to {duration}";
|
||||
@@ -2455,6 +2489,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_admin_invite_link" = "Invite users via link";
|
||||
"lng_admin_log_admin_pin_messages" = "Pin messages";
|
||||
"lng_admin_log_admin_manage_calls" = "Manage voice chats";
|
||||
"lng_admin_log_admin_manage_calls_channel" = "Manage live streams";
|
||||
"lng_admin_log_admin_add_admins" = "Add new admins";
|
||||
|
||||
"lng_terms_signup" = "By signing up,\nyou agree to the {link}.";
|
||||
@@ -2476,6 +2511,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_date_input_month" = "Month";
|
||||
"lng_date_input_year" = "Year";
|
||||
|
||||
"lng_forward_title" = "Forward Message";
|
||||
"lng_forward_many_title#one" = "Forward {count} Message";
|
||||
"lng_forward_many_title#other" = "Forward {count} Messages";
|
||||
"lng_forward_about" = "You can remove the sender's name so that this message will look like it was sent by you.";
|
||||
"lng_forward_many_about" = "You can remove the senders’ names so that these messages will look like they were sent by you.";
|
||||
"lng_forward_show_sender" = "Show sender's name";
|
||||
"lng_forward_show_senders" = "Show senders' names";
|
||||
"lng_forward_show_caption" = "Show caption";
|
||||
"lng_forward_show_captions" = "Show captions";
|
||||
"lng_forward_change_recipient" = "Change recipient";
|
||||
"lng_forward_sender_names_removed" = "Sender names removed";
|
||||
|
||||
"lng_passport_title" = "Telegram passport";
|
||||
"lng_passport_request1" = "{bot} requests access to your personal data";
|
||||
"lng_passport_request2" = "to sign you up for their services";
|
||||
|
||||
17
Telegram/Resources/langs/stale.py
Normal file
17
Telegram/Resources/langs/stale.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import os, sys, requests, re
|
||||
|
||||
os.chdir()
|
||||
|
||||
keys = []
|
||||
with open('lang.strings') as f:
|
||||
for line in f:
|
||||
m = re.match(r'\"(lng_[a-z_]+)(\#[a-z]+)?\"', line)
|
||||
if m:
|
||||
keys.append(m.group(1))
|
||||
elif not re.match(r'^\s*$', line):
|
||||
print('Bad line: ' + line)
|
||||
sys.exit(1)
|
||||
|
||||
print('Keys: ' + str(len(keys)))
|
||||
|
||||
sys.exit()
|
||||
@@ -42,7 +42,8 @@
|
||||
<file alias="js/script.js">../../export_html/js/script.js</file>
|
||||
</qresource>
|
||||
<qresource prefix="/gui">
|
||||
<file alias="art/background.jpg">../../art/background.jpg</file>
|
||||
<file alias="art/background.tgv">../../art/background.tgv</file>
|
||||
<file alias="art/bg_thumbnail.png">../../art/bg_thumbnail.png</file>
|
||||
<file alias="art/bg_initial.jpg">../../art/bg_initial.jpg</file>
|
||||
<file alias="art/logo_256.png">../../art/logo_256.png</file>
|
||||
<file alias="art/logo_256_no_margin.png">../../art/logo_256_no_margin.png</file>
|
||||
|
||||
@@ -92,7 +92,7 @@ inputPhotoFileLocation#40181ffe id:long access_hash:long file_reference:bytes th
|
||||
inputPhotoLegacyFileLocation#d83466f3 id:long access_hash:long file_reference:bytes volume_id:long local_id:int secret:long = 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;
|
||||
inputGroupCallStream#598a92a flags:# call:InputGroupCall time_ms:long scale:int video_channel:flags.0?int video_quality:flags.0?int = InputFileLocation;
|
||||
|
||||
peerUser#9db1bc6d user_id:int = Peer;
|
||||
peerChat#bad0e5bb chat_id:int = Peer;
|
||||
@@ -128,8 +128,8 @@ chatForbidden#7328bdb id:int title:string = Chat;
|
||||
channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
|
||||
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
|
||||
|
||||
chatFull#8a1e2983 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer = ChatFull;
|
||||
channelFull#548c3f93 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer = ChatFull;
|
||||
chatFull#49a0a5d9 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string = ChatFull;
|
||||
channelFull#2f532f3c flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string = ChatFull;
|
||||
|
||||
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
|
||||
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
|
||||
@@ -187,6 +187,7 @@ messageActionGroupCall#7a0d7f42 flags:# call:InputGroupCall duration:flags.0?int
|
||||
messageActionInviteToGroupCall#76b9f11a call:InputGroupCall users:Vector<int> = MessageAction;
|
||||
messageActionSetMessagesTTL#aa1afbfd period:int = MessageAction;
|
||||
messageActionGroupCallScheduled#b3a07661 call:InputGroupCall schedule_date:int = MessageAction;
|
||||
messageActionSetChatTheme#aa786345 emoticon:string = 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;
|
||||
@@ -234,7 +235,7 @@ inputReportReasonCopyright#9b89f93a = ReportReason;
|
||||
inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
|
||||
inputReportReasonFake#f5ddd6e7 = ReportReason;
|
||||
|
||||
userFull#139a9a77 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int = UserFull;
|
||||
userFull#d697ff05 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string = UserFull;
|
||||
|
||||
contact#f911c994 user_id:int mutual:Bool = Contact;
|
||||
|
||||
@@ -465,6 +466,7 @@ sendMessageRecordRoundAction#88f27fbc = SendMessageAction;
|
||||
sendMessageUploadRoundAction#243e1c66 progress:int = SendMessageAction;
|
||||
speakingInGroupCallAction#d92c2285 = SendMessageAction;
|
||||
sendMessageHistoryImportAction#dbda9246 progress:int = SendMessageAction;
|
||||
sendMessageChooseStickerAction#b05ac6b1 = SendMessageAction;
|
||||
|
||||
contacts.found#b3134d9d my_results:Vector<Peer> results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found;
|
||||
|
||||
@@ -1116,7 +1118,7 @@ restrictionReason#d072acb4 platform:string reason:string text:string = Restricti
|
||||
inputTheme#3c5693e9 id:long access_hash:long = InputTheme;
|
||||
inputThemeSlug#f5890df1 slug:string = InputTheme;
|
||||
|
||||
theme#28f1114 flags:# creator:flags.0?true default:flags.1?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?ThemeSettings installs_count:int = Theme;
|
||||
theme#e802b8dc flags:# creator:flags.0?true default:flags.1?true for_chat:flags.5?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?ThemeSettings installs_count:flags.4?int = Theme;
|
||||
|
||||
account.themesNotModified#f41eb622 = account.Themes;
|
||||
account.themes#7f676421 hash:int themes:Vector<Theme> = account.Themes;
|
||||
@@ -1135,9 +1137,9 @@ baseThemeNight#b7b31ea8 = BaseTheme;
|
||||
baseThemeTinted#6d5f77ee = BaseTheme;
|
||||
baseThemeArctic#5b11125a = BaseTheme;
|
||||
|
||||
inputThemeSettings#bd507cd1 flags:# base_theme:BaseTheme accent_color:int message_top_color:flags.0?int message_bottom_color:flags.0?int wallpaper:flags.1?InputWallPaper wallpaper_settings:flags.1?WallPaperSettings = InputThemeSettings;
|
||||
inputThemeSettings#ff38f912 flags:# message_colors_animated:flags.2?true base_theme:BaseTheme accent_color:int message_colors:flags.0?Vector<int> wallpaper:flags.1?InputWallPaper wallpaper_settings:flags.1?WallPaperSettings = InputThemeSettings;
|
||||
|
||||
themeSettings#9c14984a flags:# base_theme:BaseTheme accent_color:int message_top_color:flags.0?int message_bottom_color:flags.0?int wallpaper:flags.1?WallPaper = ThemeSettings;
|
||||
themeSettings#8db4e76c flags:# message_colors_animated:flags.2?true base_theme:BaseTheme accent_color:int message_colors:flags.0?Vector<int> wallpaper:flags.1?WallPaper = ThemeSettings;
|
||||
|
||||
webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector<Document> settings:flags.1?ThemeSettings = WebPageAttribute;
|
||||
|
||||
@@ -1195,7 +1197,7 @@ messageViews#455b853d flags:# views:flags.0?int forwards:flags.1?int replies:fla
|
||||
|
||||
messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> users:Vector<User> = messages.MessageViews;
|
||||
|
||||
messages.discussionMessage#f5dd8f9d flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
|
||||
messages.discussionMessage#a6341782 flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int unread_count:int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
|
||||
|
||||
messageReplyHeader#a6d57763 flags:# reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
|
||||
|
||||
@@ -1206,7 +1208,7 @@ 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#d597650c 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 can_start_video:flags.9?true id:long access_hash:long participants_count:int title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int unmuted_video_count:flags.10?int unmuted_video_limit:int version:int = GroupCall;
|
||||
groupCall#d597650c 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 can_start_video:flags.9?true record_video_active:flags.11?true id:long access_hash:long participants_count:int title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int unmuted_video_count:flags.10?int unmuted_video_limit:int version:int = GroupCall;
|
||||
|
||||
inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall;
|
||||
|
||||
@@ -1265,6 +1267,15 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
|
||||
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
|
||||
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
|
||||
|
||||
chatTheme#ed0b5c33 emoticon:string theme:Theme dark_theme:Theme = ChatTheme;
|
||||
|
||||
account.chatThemesNotModified#e011e1c4 = account.ChatThemes;
|
||||
account.chatThemes#fe4cbebd hash:int themes:Vector<ChatTheme> = account.ChatThemes;
|
||||
|
||||
sponsoredMessage#f671f0d1 flags:# random_id:bytes peer_id:Peer from_id:Peer message:string media:flags.0?MessageMedia entities:flags.1?Vector<MessageEntity> = SponsoredMessage;
|
||||
|
||||
messages.sponsoredMessages#65a4c7d5 messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@@ -1365,6 +1376,7 @@ account.setGlobalPrivacySettings#1edaaac2 settings:GlobalPrivacySettings = Globa
|
||||
account.reportProfilePhoto#fa8cc6f5 peer:InputPeer photo_id:InputPhoto reason:ReportReason message:string = Bool;
|
||||
account.resetPassword#9308ce1b = account.ResetPasswordResult;
|
||||
account.declinePasswordReset#4c9409f6 = Bool;
|
||||
account.getChatThemes#d6d71d7b hash:int = account.ChatThemes;
|
||||
|
||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
|
||||
@@ -1402,7 +1414,7 @@ messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
||||
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
|
||||
messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
|
||||
messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
|
||||
messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates;
|
||||
messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates;
|
||||
messages.reportSpam#cf1592db peer:InputPeer = Bool;
|
||||
messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings;
|
||||
messages.report#8953ab4e peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool;
|
||||
@@ -1533,6 +1545,7 @@ messages.getAdminsWithInvites#3920e6ef peer:InputPeer = messages.ChatAdminsWithI
|
||||
messages.getChatInviteImporters#26fb7289 peer:InputPeer link:string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters;
|
||||
messages.setHistoryTTL#b80e5fe4 peer:InputPeer period:int = Updates;
|
||||
messages.checkHistoryImportPeer#5dc60f03 peer:InputPeer = messages.CheckedHistoryImportPeer;
|
||||
messages.setChatTheme#e63be13f peer:InputPeer emoticon:string = Updates;
|
||||
|
||||
updates.getState#edd4882a = updates.State;
|
||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||
@@ -1611,6 +1624,8 @@ channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint addr
|
||||
channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates;
|
||||
channels.getInactiveChannels#11e831ee = messages.InactiveChats;
|
||||
channels.convertToGigagroup#b290c69 channel:InputChannel = Updates;
|
||||
channels.viewSponsoredMessage#beaedb94 channel:InputChannel random_id:bytes = Bool;
|
||||
channels.getSponsoredMessages#ec210fbf channel:InputChannel = messages.SponsoredMessages;
|
||||
|
||||
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
||||
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
||||
@@ -1649,10 +1664,10 @@ phone.leaveGroupCall#500377f9 call:InputGroupCall source:int = Updates;
|
||||
phone.inviteToGroupCall#7b393160 call:InputGroupCall users:Vector<InputUser> = Updates;
|
||||
phone.discardGroupCall#7a777135 call:InputGroupCall = Updates;
|
||||
phone.toggleGroupCallSettings#74bbb43d flags:# reset_invite_hash:flags.1?true call:InputGroupCall join_muted:flags.0?Bool = Updates;
|
||||
phone.getGroupCall#c7cb017 call:InputGroupCall = phone.GroupCall;
|
||||
phone.getGroupCall#41845db call:InputGroupCall limit:int = phone.GroupCall;
|
||||
phone.getGroupParticipants#c558d8ab call:InputGroupCall ids:Vector<InputPeer> sources:Vector<int> offset:string limit:int = phone.GroupParticipants;
|
||||
phone.checkGroupCall#b59cf977 call:InputGroupCall sources:Vector<int> = Vector<int>;
|
||||
phone.toggleGroupCallRecord#c02a66d7 flags:# start:flags.0?true call:InputGroupCall title:flags.1?string = Updates;
|
||||
phone.toggleGroupCallRecord#f128c708 flags:# start:flags.0?true video:flags.2?true call:InputGroupCall title:flags.1?string video_portrait:flags.2?Bool = Updates;
|
||||
phone.editGroupCallParticipant#a5273abf flags:# call:InputGroupCall participant:InputPeer muted:flags.0?Bool volume:flags.1?int raise_hand:flags.2?Bool video_stopped:flags.3?Bool video_paused:flags.4?Bool presentation_paused:flags.5?Bool = Updates;
|
||||
phone.editGroupCallTitle#1ca6ac0a call:InputGroupCall title:string = Updates;
|
||||
phone.getGroupCallJoinAs#ef7c213a peer:InputPeer = phone.JoinAsPeers;
|
||||
@@ -1678,4 +1693,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 131
|
||||
// LAYER 132
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="2.9.7.0" />
|
||||
Version="3.0.1.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,9,7,0
|
||||
PRODUCTVERSION 2,9,7,0
|
||||
FILEVERSION 3,0,1,0
|
||||
PRODUCTVERSION 3,0,1,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.9.7.0"
|
||||
VALUE "FileVersion", "3.0.1.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.9.7.0"
|
||||
VALUE "ProductVersion", "3.0.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,9,7,0
|
||||
PRODUCTVERSION 2,9,7,0
|
||||
FILEVERSION 3,0,1,0
|
||||
PRODUCTVERSION 3,0,1,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.9.7.0"
|
||||
VALUE "FileVersion", "3.0.1.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.9.7.0"
|
||||
VALUE "ProductVersion", "3.0.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
class UserData;
|
||||
class ChannelData;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
@@ -129,6 +129,7 @@ void SendProgressManager::send(const Key &key, int progress) {
|
||||
case Type::ChooseContact: return MTP_sendMessageChooseContactAction();
|
||||
case Type::PlayGame: return MTP_sendMessageGamePlayAction();
|
||||
case Type::Speaking: return MTP_speakingInGroupCallAction();
|
||||
case Type::ChooseSticker: return MTP_sendMessageChooseStickerAction();
|
||||
default: return MTP_sendMessageTypingAction();
|
||||
}
|
||||
}();
|
||||
|
||||
@@ -30,6 +30,7 @@ enum class SendProgressType {
|
||||
UploadFile,
|
||||
ChooseLocation,
|
||||
ChooseContact,
|
||||
ChooseSticker,
|
||||
PlayGame,
|
||||
Speaking,
|
||||
};
|
||||
|
||||
@@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
class History;
|
||||
class PhotoData;
|
||||
class DocumentData;
|
||||
|
||||
@@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_scheduled_messages.h"
|
||||
#include "data/data_send_action.h"
|
||||
#include "lang/lang_cloud_manager.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
@@ -1017,7 +1018,7 @@ void Updates::handleSendActionUpdate(
|
||||
const auto when = requestingDifference()
|
||||
? 0
|
||||
: base::unixtime::now();
|
||||
session().data().registerSendAction(
|
||||
session().data().sendActionManager().registerFor(
|
||||
history,
|
||||
rootId,
|
||||
from->asUser(),
|
||||
@@ -2094,17 +2095,20 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
const auto msgId = d.vtop_msg_id().v;
|
||||
const auto readTillId = d.vread_max_id().v;
|
||||
const auto item = session().data().message(channelId, msgId);
|
||||
const auto unreadCount = item
|
||||
? session().data().countUnreadRepliesLocally(item, readTillId)
|
||||
: std::nullopt;
|
||||
if (item) {
|
||||
item->setRepliesInboxReadTill(readTillId);
|
||||
item->setRepliesInboxReadTill(readTillId, unreadCount);
|
||||
if (const auto post = item->lookupDiscussionPostOriginal()) {
|
||||
post->setRepliesInboxReadTill(readTillId);
|
||||
post->setRepliesInboxReadTill(readTillId, unreadCount);
|
||||
}
|
||||
}
|
||||
if (const auto broadcastId = d.vbroadcast_id()) {
|
||||
if (const auto post = session().data().message(
|
||||
broadcastId->v,
|
||||
d.vbroadcast_post()->v)) {
|
||||
post->setRepliesInboxReadTill(readTillId);
|
||||
post->setRepliesInboxReadTill(readTillId, unreadCount);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -3505,7 +3505,7 @@ void ApiWrap::sharedMediaDone(
|
||||
parsed.fullCount
|
||||
));
|
||||
if (type == SharedMediaType::Pinned && !parsed.messageIds.empty()) {
|
||||
peer->setHasPinnedMessages(true);
|
||||
peer->owner().history(peer)->setHasPinnedMessages(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3580,9 +3580,11 @@ void ApiWrap::sendAction(const SendAction &action) {
|
||||
|
||||
void ApiWrap::finishForwarding(const SendAction &action) {
|
||||
const auto history = action.history;
|
||||
auto toForward = history->validateForwardDraft();
|
||||
if (!toForward.empty()) {
|
||||
const auto error = GetErrorTextForSending(history->peer, toForward);
|
||||
auto toForward = history->resolveForwardDraft();
|
||||
if (!toForward.items.empty()) {
|
||||
const auto error = GetErrorTextForSending(
|
||||
history->peer,
|
||||
toForward.items);
|
||||
if (!error.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -3600,10 +3602,10 @@ void ApiWrap::finishForwarding(const SendAction &action) {
|
||||
}
|
||||
|
||||
void ApiWrap::forwardMessages(
|
||||
HistoryItemsList &&items,
|
||||
Data::ResolvedForwardDraft &&draft,
|
||||
const SendAction &action,
|
||||
FnMut<void()> &&successCallback) {
|
||||
Expects(!items.empty());
|
||||
Expects(!draft.items.empty());
|
||||
|
||||
auto &histories = _session->data().histories();
|
||||
|
||||
@@ -3618,8 +3620,10 @@ void ApiWrap::forwardMessages(
|
||||
shared->callback = std::move(successCallback);
|
||||
}
|
||||
|
||||
const auto count = int(items.size());
|
||||
const auto genClientSideMessage = action.generateLocal && (count < 2);
|
||||
const auto count = int(draft.items.size());
|
||||
const auto genClientSideMessage = action.generateLocal
|
||||
&& (count < 2)
|
||||
&& (draft.options == Data::ForwardOptions::PreserveInfo);
|
||||
const auto history = action.history;
|
||||
const auto peer = history->peer;
|
||||
|
||||
@@ -3640,8 +3644,14 @@ void ApiWrap::forwardMessages(
|
||||
} else {
|
||||
flags |= MessageFlag::LocalHistoryEntry;
|
||||
}
|
||||
if (draft.options != Data::ForwardOptions::PreserveInfo) {
|
||||
sendFlags |= MTPmessages_ForwardMessages::Flag::f_drop_author;
|
||||
}
|
||||
if (draft.options == Data::ForwardOptions::NoNamesAndCaptions) {
|
||||
sendFlags |= MTPmessages_ForwardMessages::Flag::f_drop_media_captions;
|
||||
}
|
||||
|
||||
auto forwardFrom = items.front()->history()->peer;
|
||||
auto forwardFrom = draft.items.front()->history()->peer;
|
||||
auto ids = QVector<MTPint>();
|
||||
auto randomIds = QVector<MTPlong>();
|
||||
auto localIds = std::shared_ptr<base::flat_map<uint64, FullMsgId>>();
|
||||
@@ -3688,7 +3698,7 @@ void ApiWrap::forwardMessages(
|
||||
|
||||
ids.reserve(count);
|
||||
randomIds.reserve(count);
|
||||
for (const auto item : items) {
|
||||
for (const auto item : draft.items) {
|
||||
const auto randomId = openssl::RandomValue<uint64>();
|
||||
if (genClientSideMessage) {
|
||||
if (const auto message = item->toHistoryMessage()) {
|
||||
|
||||
@@ -29,6 +29,7 @@ class Session;
|
||||
namespace Data {
|
||||
struct UpdatedFileReferences;
|
||||
class WallPaper;
|
||||
struct ResolvedForwardDraft;
|
||||
} // namespace Data
|
||||
|
||||
namespace InlineBots {
|
||||
@@ -319,7 +320,7 @@ public:
|
||||
void sendAction(const SendAction &action);
|
||||
void finishForwarding(const SendAction &action);
|
||||
void forwardMessages(
|
||||
HistoryItemsList &&items,
|
||||
Data::ResolvedForwardDraft &&draft,
|
||||
const SendAction &action,
|
||||
FnMut<void()> &&successCallback = nullptr);
|
||||
void shareContact(
|
||||
|
||||
@@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/flat_set.h"
|
||||
#include "base/openssl_help.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/confirm_phone_box.h" // ExtractPhonePrefix.
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/peers/add_participants_box.h"
|
||||
#include "boxes/peers/edit_participant_box.h"
|
||||
@@ -20,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/application.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "countries/countries_instance.h" // Countries::ExtractPhoneCode.
|
||||
#include "window/window_session_controller.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@@ -267,7 +267,7 @@ AddContactBox::AddContactBox(
|
||||
this,
|
||||
st::defaultInputField,
|
||||
tr::lng_contact_phone(),
|
||||
Ui::ExtractPhonePrefix(session->user()->phone()),
|
||||
Countries::ExtractPhoneCode(session->user()->phone()),
|
||||
phone)
|
||||
, _invertOrder(langFirstNameGoesSecond()) {
|
||||
if (!phone.isEmpty()) {
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/effects/round_checkbox.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
@@ -241,6 +242,7 @@ void BackgroundBox::Inner::sortPapers() {
|
||||
night ? data.isDark() : !data.isDark(),
|
||||
Data::IsDefaultWallPaper(data),
|
||||
!data.isDefault() && !Data::IsLegacy1DefaultWallPaper(data),
|
||||
Data::IsLegacy3DefaultWallPaper(data),
|
||||
Data::IsLegacy2DefaultWallPaper(data),
|
||||
Data::IsLegacy1DefaultWallPaper(data));
|
||||
});
|
||||
@@ -331,7 +333,7 @@ void BackgroundBox::Inner::validatePaperThumbnail(
|
||||
}
|
||||
} else if (!paper.data.backgroundColors().empty()) {
|
||||
paper.thumbnail = Ui::PixmapFromImage(
|
||||
Data::GenerateWallPaper(
|
||||
Ui::GenerateBackgroundImage(
|
||||
st::backgroundSize * cIntRetinaFactor(),
|
||||
paper.data.backgroundColors(),
|
||||
paper.data.gradientRotation()));
|
||||
@@ -346,7 +348,7 @@ void BackgroundBox::Inner::validatePaperThumbnail(
|
||||
: paper.dataMedia->thumbnail();
|
||||
auto original = thumbnail->original();
|
||||
if (paper.data.isPattern()) {
|
||||
original = Data::PreparePatternImage(
|
||||
original = Ui::PreparePatternImage(
|
||||
std::move(original),
|
||||
paper.data.backgroundColors(),
|
||||
paper.data.gradientRotation(),
|
||||
@@ -378,6 +380,7 @@ void BackgroundBox::Inner::paintPaper(
|
||||
} else if (Data::IsCloudWallPaper(paper.data)
|
||||
&& !Data::IsDefaultWallPaper(paper.data)
|
||||
&& !Data::IsLegacy2DefaultWallPaper(paper.data)
|
||||
&& !Data::IsLegacy3DefaultWallPaper(paper.data)
|
||||
&& !v::is_null(over)
|
||||
&& (&paper == &_papers[getSelectionIndex(over)])) {
|
||||
const auto deleteSelected = v::is<DeleteSelected>(over);
|
||||
@@ -418,6 +421,7 @@ void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) {
|
||||
&& Data::IsCloudWallPaper(data)
|
||||
&& !Data::IsDefaultWallPaper(data)
|
||||
&& !Data::IsLegacy2DefaultWallPaper(data)
|
||||
&& !Data::IsLegacy3DefaultWallPaper(data)
|
||||
&& (currentId != data.id());
|
||||
return (result >= _papers.size())
|
||||
? Selection()
|
||||
|
||||
@@ -10,6 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
@@ -340,7 +342,7 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
|
||||
Images::Option blur = Images::Option(0)) {
|
||||
auto result = PrepareScaledNonPattern(image, blur);
|
||||
if (isPattern) {
|
||||
result = Data::PreparePatternImage(
|
||||
result = Ui::PreparePatternImage(
|
||||
std::move(result),
|
||||
background,
|
||||
gradientRotation,
|
||||
@@ -364,6 +366,7 @@ BackgroundPreviewBox::BackgroundPreviewBox(
|
||||
const Data::WallPaper &paper)
|
||||
: SimpleElementDelegate(controller, [=] { update(); })
|
||||
, _controller(controller)
|
||||
, _chatStyle(std::make_unique<Ui::ChatStyle>())
|
||||
, _text1(GenerateTextItem(
|
||||
delegate(),
|
||||
_controller->session().data().history(PeerData::kServiceNotificationsId),
|
||||
@@ -377,6 +380,8 @@ BackgroundPreviewBox::BackgroundPreviewBox(
|
||||
, _paper(paper)
|
||||
, _media(_paper.document() ? _paper.document()->createMediaView() : nullptr)
|
||||
, _radial([=](crl::time now) { radialAnimationCallback(now); }) {
|
||||
_chatStyle->apply(controller->defaultChatTheme().get());
|
||||
|
||||
if (_media) {
|
||||
_media->thumbnailWanted(_paper.fileOrigin());
|
||||
}
|
||||
@@ -394,7 +399,7 @@ void BackgroundPreviewBox::generateBackground() {
|
||||
const auto size = QSize(st::boxWideWidth, st::boxWideWidth)
|
||||
* cIntRetinaFactor();
|
||||
_generated = Ui::PixmapFromImage((_paper.patternOpacity() >= 0.)
|
||||
? Data::GenerateWallPaper(
|
||||
? Ui::GenerateBackgroundImage(
|
||||
size,
|
||||
_paper.backgroundColors(),
|
||||
_paper.gradientRotation())
|
||||
@@ -589,13 +594,10 @@ QRect BackgroundPreviewBox::radialRect() const {
|
||||
void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
|
||||
const auto height1 = _text1->height();
|
||||
const auto height2 = _text2->height();
|
||||
const auto context = HistoryView::PaintContext{
|
||||
.bubblesPattern = nullptr, // #TODO bubbles
|
||||
.viewport = rect(),
|
||||
.clip = rect(),
|
||||
.selection = TextSelection(),
|
||||
.now = ms,
|
||||
};
|
||||
const auto context = _controller->defaultChatTheme()->preparePaintContext(
|
||||
_chatStyle.get(),
|
||||
rect(),
|
||||
rect());
|
||||
p.translate(0, textsTop());
|
||||
paintDate(p);
|
||||
_text1->draw(p, context);
|
||||
@@ -666,7 +668,7 @@ void BackgroundPreviewBox::setScaledFromThumb() {
|
||||
auto blurred = (_paper.document() || _paper.isPattern())
|
||||
? QImage()
|
||||
: PrepareScaledNonPattern(
|
||||
Data::PrepareBlurredBackground(thumbnail->original()),
|
||||
Ui::PrepareBlurredBackground(thumbnail->original()),
|
||||
Images::Option(0));
|
||||
setScaledFromImage(std::move(scaled), std::move(blurred));
|
||||
}
|
||||
@@ -674,7 +676,7 @@ void BackgroundPreviewBox::setScaledFromThumb() {
|
||||
void BackgroundPreviewBox::setScaledFromImage(
|
||||
QImage &&image,
|
||||
QImage &&blurred) {
|
||||
updateServiceBg({ Window::Theme::CountAverageColor(image) });
|
||||
updateServiceBg({ Ui::CountAverageColor(image) });
|
||||
if (!_full.isNull()) {
|
||||
startFadeInFrom(std::move(_scaled));
|
||||
}
|
||||
@@ -712,7 +714,7 @@ void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
|
||||
green += color.green();
|
||||
blue += color.blue();
|
||||
}
|
||||
_serviceBg = Window::Theme::AdjustedColor(
|
||||
_serviceBg = Ui::ThemeAdjustedColor(
|
||||
st::msgServiceBg->c,
|
||||
QColor(red / count, green / count, blue / count));
|
||||
}
|
||||
@@ -746,7 +748,7 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
||||
patternOpacity);
|
||||
auto blurred = !isPattern
|
||||
? PrepareScaledNonPattern(
|
||||
Data::PrepareBlurredBackground(image),
|
||||
Ui::PrepareBlurredBackground(image),
|
||||
Images::Option(0))
|
||||
: QImage();
|
||||
crl::on_main(std::move(guard), [
|
||||
@@ -761,9 +763,9 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
||||
});
|
||||
});
|
||||
};
|
||||
_generating = Data::ReadImageAsync(
|
||||
_generating = Data::ReadBackgroundImageAsync(
|
||||
_media.get(),
|
||||
Window::Theme::PreprocessBackgroundImage,
|
||||
Ui::PreprocessBackgroundImage,
|
||||
generateCallback);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ class SessionController;
|
||||
|
||||
namespace Ui {
|
||||
class Checkbox;
|
||||
class ChatStyle;
|
||||
} // namespace Ui
|
||||
|
||||
class BackgroundPreviewBox
|
||||
@@ -71,6 +72,7 @@ private:
|
||||
void checkBlurAnimationStart();
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
std::unique_ptr<Ui::ChatStyle> _chatStyle;
|
||||
AdminLog::OwnedItem _text1;
|
||||
AdminLog::OwnedItem _text2;
|
||||
Data::WallPaper _paper;
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/special_fields.h"
|
||||
#include "boxes/confirm_phone_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "countries/countries_instance.h" // Countries::ExtractPhoneCode.
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
@@ -151,7 +152,7 @@ void ChangePhoneBox::EnterPhone::prepare() {
|
||||
this,
|
||||
st::defaultInputField,
|
||||
tr::lng_change_phone_new_title(),
|
||||
Ui::ExtractPhonePrefix(_session->user()->phone()),
|
||||
Countries::ExtractPhoneCode(_session->user()->phone()),
|
||||
phoneValue);
|
||||
|
||||
_phone->resize(st::boxWidth - 2 * st::boxPadding.left(), _phone->height());
|
||||
|
||||
@@ -281,7 +281,8 @@ void ConfirmBox::confirmed() {
|
||||
}
|
||||
} else if (const auto callbackPtr = std::get_if<2>(confirmed)) {
|
||||
if (auto callback = base::take(*callbackPtr)) {
|
||||
callback([=] { closeBox(); });
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
callback(crl::guard(weak, [=] { closeBox(); }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "main/main_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "numbers.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mtproto/facade.h"
|
||||
#include "styles/style_layers.h"
|
||||
|
||||
@@ -150,7 +150,7 @@ std::vector<std::pair<ChatAdminRights, QString>> AdminRightLabels(
|
||||
{ Flag::EditMessages, tr::lng_rights_channel_edit(tr::now) },
|
||||
{ Flag::DeleteMessages, tr::lng_rights_channel_delete(tr::now) },
|
||||
{ Flag::InviteUsers, tr::lng_rights_group_invite(tr::now) },
|
||||
{ Flag::ManageCall, tr::lng_rights_group_manage_calls(tr::now) },
|
||||
{ Flag::ManageCall, tr::lng_rights_channel_manage_calls(tr::now) },
|
||||
{ Flag::AddAdmins, tr::lng_rights_add_admins(tr::now) }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1902,14 +1902,17 @@ void StickersBox::Inner::handleMegagroupSetAddressChange() {
|
||||
void StickersBox::Inner::rebuildMegagroupSet() {
|
||||
Expects(_megagroupSet != nullptr);
|
||||
|
||||
if (!_megagroupSetInput.id) {
|
||||
const auto clearCurrent = [&] {
|
||||
if (_megagroupSelectedSet) {
|
||||
_megagroupSetField->setText(QString());
|
||||
_megagroupSetField->finishAnimating();
|
||||
}
|
||||
_megagroupSelectedSet.reset();
|
||||
_megagroupSelectedSet = nullptr;
|
||||
_megagroupSelectedRemove.destroy();
|
||||
_megagroupSelectedShadow.destroy();
|
||||
};
|
||||
if (!_megagroupSetInput.id) {
|
||||
clearCurrent();
|
||||
return;
|
||||
}
|
||||
auto setId = _megagroupSetInput.id;
|
||||
@@ -1917,6 +1920,10 @@ void StickersBox::Inner::rebuildMegagroupSet() {
|
||||
auto it = sets.find(setId);
|
||||
if (it == sets.cend()
|
||||
|| (it->second->flags & SetFlag::NotLoaded)) {
|
||||
// It may have been in sets and stored in _megagroupSelectedSet
|
||||
// already, but then removed from sets. We need to clear the stored
|
||||
// pointer, otherwise we may crash in paint event while loading.
|
||||
clearCurrent();
|
||||
session().api().scheduleStickerSetRequest(
|
||||
_megagroupSetInput.id,
|
||||
_megagroupSetInput.accessHash);
|
||||
|
||||
@@ -126,6 +126,7 @@ void ScheduleGroupCallBox(
|
||||
copy.scheduleDate = date;
|
||||
done(std::move(copy));
|
||||
};
|
||||
const auto livestream = info.peer->isBroadcast();
|
||||
const auto duration = box->lifetime().make_state<
|
||||
rpl::variable<QString>>();
|
||||
auto description = (info.peer->isBroadcast()
|
||||
@@ -151,7 +152,9 @@ void ScheduleGroupCallBox(
|
||||
).addSecs(60 * 60 * (now.time().minute() < 30 ? 1 : 2));
|
||||
|
||||
auto descriptor = Ui::ChooseDateTimeBox(box, {
|
||||
.title = tr::lng_group_call_schedule_title(),
|
||||
.title = (livestream
|
||||
? tr::lng_group_call_schedule_title_channel()
|
||||
: tr::lng_group_call_schedule_title()),
|
||||
.submit = tr::lng_schedule_button(),
|
||||
.done = send,
|
||||
.min = min,
|
||||
@@ -194,11 +197,16 @@ void ChooseJoinAsBox(
|
||||
JoinInfo info,
|
||||
Fn<void(JoinInfo)> done) {
|
||||
box->setWidth(st::groupCallJoinAsWidth);
|
||||
const auto livestream = info.peer->isBroadcast();
|
||||
box->setTitle([&] {
|
||||
switch (context) {
|
||||
case Context::Create: return tr::lng_group_call_start_as_header();
|
||||
case Context::Create: return livestream
|
||||
? tr::lng_group_call_start_as_header_channel()
|
||||
: tr::lng_group_call_start_as_header();
|
||||
case Context::Join:
|
||||
case Context::JoinWithConfirm: return tr::lng_group_call_join_as_header();
|
||||
case Context::JoinWithConfirm: return livestream
|
||||
? tr::lng_group_call_join_as_header_channel()
|
||||
: tr::lng_group_call_join_as_header();
|
||||
case Context::Switch: return tr::lng_group_call_display_as_header();
|
||||
}
|
||||
Unexpected("Context in ChooseJoinAsBox.");
|
||||
@@ -243,7 +251,9 @@ void ChooseJoinAsBox(
|
||||
box,
|
||||
tr::lng_group_call_or_schedule(
|
||||
lt_link,
|
||||
tr::lng_group_call_schedule(makeLink),
|
||||
(livestream
|
||||
? tr::lng_group_call_schedule_channel
|
||||
: tr::lng_group_call_schedule)(makeLink),
|
||||
Ui::Text::WithEntities),
|
||||
labelSt));
|
||||
label->setClickHandlerFilter([=](const auto&...) {
|
||||
@@ -276,8 +286,7 @@ void ChooseJoinAsBox(
|
||||
const auto anonymouseAdmin = channel
|
||||
&& ((channel->isMegagroup() && channel->amAnonymous())
|
||||
|| (channel->isBroadcast()
|
||||
&& (channel->amCreator()
|
||||
|| channel->hasAdminRights())));
|
||||
&& (channel->amCreator() || channel->hasAdminRights())));
|
||||
if (anonymouseAdmin && !joinAsAlreadyUsed) {
|
||||
return { tr::lng_group_call_join_sure_personal(tr::now) };
|
||||
} else if (context != ChooseJoinAsProcess::Context::JoinWithConfirm) {
|
||||
@@ -286,7 +295,9 @@ void ChooseJoinAsBox(
|
||||
const auto name = !existing->title().isEmpty()
|
||||
? existing->title()
|
||||
: peer->name;
|
||||
return tr::lng_group_call_join_confirm(
|
||||
return (peer->isBroadcast()
|
||||
? tr::lng_group_call_join_confirm_channel
|
||||
: tr::lng_group_call_join_confirm)(
|
||||
tr::now,
|
||||
lt_chat,
|
||||
Ui::Text::Bold(name),
|
||||
@@ -402,6 +413,7 @@ void ChooseJoinAsProcess::start(
|
||||
finish(info);
|
||||
return;
|
||||
}
|
||||
const auto livestream = peer->isBroadcast();
|
||||
const auto creating = !peer->groupCall();
|
||||
if (creating) {
|
||||
confirmation
|
||||
@@ -409,7 +421,9 @@ void ChooseJoinAsProcess::start(
|
||||
.append(tr::lng_group_call_or_schedule(
|
||||
tr::now,
|
||||
lt_link,
|
||||
Ui::Text::Link(tr::lng_group_call_schedule(tr::now)),
|
||||
Ui::Text::Link((livestream
|
||||
? tr::lng_group_call_schedule_channel
|
||||
: tr::lng_group_call_schedule)(tr::now)),
|
||||
Ui::Text::WithEntities));
|
||||
}
|
||||
const auto guard = base::make_weak(&_request->guard);
|
||||
|
||||
@@ -126,11 +126,19 @@ using JoinClientFields = std::variant<
|
||||
|
||||
class GroupCall::LoadPartTask final : public tgcalls::BroadcastPartTask {
|
||||
public:
|
||||
using Quality = tgcalls::VideoChannelDescription::Quality;
|
||||
LoadPartTask(
|
||||
base::weak_ptr<GroupCall> call,
|
||||
int64 time,
|
||||
int64 period,
|
||||
Fn<void(tgcalls::BroadcastPart&&)> done);
|
||||
LoadPartTask(
|
||||
base::weak_ptr<GroupCall> call,
|
||||
int64 time,
|
||||
int64 period,
|
||||
int32 videoChannel,
|
||||
Quality videoQuality,
|
||||
Fn<void(tgcalls::BroadcastPart&&)> done);
|
||||
|
||||
[[nodiscard]] int64 time() const {
|
||||
return _time;
|
||||
@@ -138,6 +146,12 @@ public:
|
||||
[[nodiscard]] int32 scale() const {
|
||||
return _scale;
|
||||
}
|
||||
[[nodiscard]] int32 videoChannel() const {
|
||||
return _videoChannel;
|
||||
}
|
||||
[[nodiscard]] Quality videoQuality() const {
|
||||
return _videoQuality;
|
||||
}
|
||||
|
||||
void done(tgcalls::BroadcastPart &&part);
|
||||
void cancel() override;
|
||||
@@ -146,6 +160,8 @@ private:
|
||||
const base::weak_ptr<GroupCall> _call;
|
||||
const int64 _time = 0;
|
||||
const int32 _scale = 0;
|
||||
const int32 _videoChannel = 0;
|
||||
const Quality _videoQuality = {};
|
||||
Fn<void(tgcalls::BroadcastPart &&)> _done;
|
||||
QMutex _mutex;
|
||||
|
||||
@@ -379,7 +395,17 @@ GroupCall::LoadPartTask::LoadPartTask(
|
||||
base::weak_ptr<GroupCall> call,
|
||||
int64 time,
|
||||
int64 period,
|
||||
Fn<void(tgcalls::BroadcastPart &&)> done)
|
||||
Fn<void(tgcalls::BroadcastPart&&)> done)
|
||||
: LoadPartTask(std::move(call), time, period, 0, {}, std::move(done)) {
|
||||
}
|
||||
|
||||
GroupCall::LoadPartTask::LoadPartTask(
|
||||
base::weak_ptr<GroupCall> call,
|
||||
int64 time,
|
||||
int64 period,
|
||||
int32 videoChannel,
|
||||
tgcalls::VideoChannelDescription::Quality videoQuality,
|
||||
Fn<void(tgcalls::BroadcastPart&&)> done)
|
||||
: _call(std::move(call))
|
||||
, _time(time ? time : (base::unixtime::now() * int64(1000)))
|
||||
, _scale([&] {
|
||||
@@ -391,6 +417,8 @@ GroupCall::LoadPartTask::LoadPartTask(
|
||||
}
|
||||
Unexpected("Period in LoadPartTask.");
|
||||
}())
|
||||
, _videoChannel(videoChannel)
|
||||
, _videoQuality(videoQuality)
|
||||
, _done(std::move(done)) {
|
||||
}
|
||||
|
||||
@@ -857,9 +885,6 @@ void GroupCall::setState(State state) {
|
||||
if (const auto call = _peer->groupCall(); call && call->id() == _id) {
|
||||
call->setInCall();
|
||||
}
|
||||
if (!videoIsWorking()) {
|
||||
refreshHasNotShownVideo();
|
||||
}
|
||||
}
|
||||
|
||||
if (false
|
||||
@@ -954,11 +979,6 @@ void GroupCall::start(TimeId scheduleDate) {
|
||||
LOG(("Call Error: Could not create, error: %1"
|
||||
).arg(error.type()));
|
||||
hangup();
|
||||
if (error.type() == u"GROUPCALL_ANONYMOUS_FORBIDDEN"_q) {
|
||||
Ui::ShowMultilineToast({
|
||||
.text = { tr::lng_group_call_no_anonymous(tr::now) },
|
||||
});
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
@@ -1072,9 +1092,6 @@ void GroupCall::markEndpointActive(
|
||||
bool paused) {
|
||||
if (!endpoint) {
|
||||
return;
|
||||
} else if (active && !videoIsWorking()) {
|
||||
refreshHasNotShownVideo();
|
||||
return;
|
||||
}
|
||||
const auto i = _activeVideoTracks.find(endpoint);
|
||||
const auto changed = active
|
||||
@@ -1291,11 +1308,7 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
|
||||
|
||||
hangup();
|
||||
Ui::ShowMultilineToast({
|
||||
.text = { type == u"GROUPCALL_ANONYMOUS_FORBIDDEN"_q
|
||||
? tr::lng_group_call_no_anonymous(tr::now)
|
||||
: type == u"GROUPCALL_PARTICIPANTS_TOO_MUCH"_q
|
||||
? tr::lng_group_call_too_many(tr::now)
|
||||
: type == u"GROUPCALL_FORBIDDEN"_q
|
||||
.text = { type == u"GROUPCALL_FORBIDDEN"_q
|
||||
? tr::lng_group_not_accessible(tr::now)
|
||||
: Lang::Hard::ServerError() },
|
||||
});
|
||||
@@ -2190,7 +2203,8 @@ void GroupCall::toggleRecording(bool enabled, const QString &title) {
|
||||
MTP_flags((enabled ? Flag::f_start : Flag(0))
|
||||
| (title.isEmpty() ? Flag(0) : Flag::f_title)),
|
||||
inputCall(),
|
||||
MTP_string(title)
|
||||
MTP_string(title),
|
||||
MTPBool() // video_portrait
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_peer->session().api().applyUpdates(result);
|
||||
_recordingStoppedByMe = false;
|
||||
@@ -2234,7 +2248,7 @@ bool GroupCall::tryCreateController() {
|
||||
.createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator(
|
||||
settings.callAudioBackend()),
|
||||
.videoCapture = _cameraCapture,
|
||||
.requestBroadcastPart = [=, call = base::make_weak(this)](
|
||||
.requestAudioBroadcastPart = [=, call = base::make_weak(this)](
|
||||
int64_t time,
|
||||
int64_t period,
|
||||
std::function<void(tgcalls::BroadcastPart &&)> done) {
|
||||
@@ -2248,6 +2262,24 @@ bool GroupCall::tryCreateController() {
|
||||
});
|
||||
return result;
|
||||
},
|
||||
.requestVideoBroadcastPart = [=, call = base::make_weak(this)](
|
||||
int64_t time,
|
||||
int64_t period,
|
||||
int32_t channel,
|
||||
tgcalls::VideoChannelDescription::Quality quality,
|
||||
std::function<void(tgcalls::BroadcastPart &&)> done) {
|
||||
auto result = std::make_shared<LoadPartTask>(
|
||||
call,
|
||||
time,
|
||||
period,
|
||||
channel,
|
||||
quality,
|
||||
std::move(done));
|
||||
crl::on_main(weak, [=]() mutable {
|
||||
broadcastPartStart(std::move(result));
|
||||
});
|
||||
return result;
|
||||
},
|
||||
.videoContentType = tgcalls::VideoContentType::Generic,
|
||||
.initialEnableNoiseSuppression
|
||||
= settings.groupCallNoiseSuppression(),
|
||||
@@ -2326,17 +2358,30 @@ void GroupCall::broadcastPartStart(std::shared_ptr<LoadPartTask> task) {
|
||||
const auto raw = task.get();
|
||||
const auto time = raw->time();
|
||||
const auto scale = raw->scale();
|
||||
const auto videoChannel = raw->videoChannel();
|
||||
const auto videoQuality = raw->videoQuality();
|
||||
const auto finish = [=](tgcalls::BroadcastPart &&part) {
|
||||
raw->done(std::move(part));
|
||||
_broadcastParts.erase(raw);
|
||||
};
|
||||
using Status = tgcalls::BroadcastPart::Status;
|
||||
using Quality = tgcalls::VideoChannelDescription::Quality;
|
||||
using Flag = MTPDinputGroupCallStream::Flag;
|
||||
const auto requestId = _api.request(MTPupload_GetFile(
|
||||
MTP_flags(0),
|
||||
MTP_inputGroupCallStream(
|
||||
MTP_flags(videoChannel
|
||||
? (Flag::f_video_channel | Flag::f_video_quality)
|
||||
: Flag(0)),
|
||||
inputCall(),
|
||||
MTP_long(time),
|
||||
MTP_int(scale)),
|
||||
MTP_int(scale),
|
||||
MTP_int(videoChannel),
|
||||
MTP_int((videoQuality == Quality::Full)
|
||||
? 2
|
||||
: (videoQuality == Quality::Medium)
|
||||
? 1
|
||||
: 0)),
|
||||
MTP_int(0),
|
||||
MTP_int(128 * 1024)
|
||||
)).done([=](
|
||||
@@ -2350,7 +2395,7 @@ void GroupCall::broadcastPartStart(std::shared_ptr<LoadPartTask> task) {
|
||||
.timestampMilliseconds = time,
|
||||
.responseTimestamp = TimestampFromMsgId(response.outerMsgId),
|
||||
.status = Status::Success,
|
||||
.oggData = std::move(bytes),
|
||||
.data = std::move(bytes),
|
||||
});
|
||||
}, [&](const MTPDupload_fileCdnRedirect &data) {
|
||||
LOG(("Voice Chat Stream Error: fileCdnRedirect received."));
|
||||
@@ -2558,22 +2603,6 @@ void GroupCall::updateRequestedVideoChannelsDelayed() {
|
||||
});
|
||||
}
|
||||
|
||||
void GroupCall::refreshHasNotShownVideo() {
|
||||
if (!_joinState.ssrc || hasNotShownVideo()) {
|
||||
return;
|
||||
}
|
||||
const auto real = lookupReal();
|
||||
Assert(real != nullptr);
|
||||
|
||||
const auto hasVideo = [&](const Data::GroupCallParticipant &data) {
|
||||
return (data.peer != _joinAs)
|
||||
&& (!GetCameraEndpoint(data.videoParams).empty()
|
||||
|| !GetScreenEndpoint(data.videoParams).empty());
|
||||
};
|
||||
_hasNotShownVideo = _joinState.ssrc
|
||||
&& ranges::any_of(real->participants(), hasVideo);
|
||||
}
|
||||
|
||||
void GroupCall::fillActiveVideoEndpoints() {
|
||||
const auto real = lookupReal();
|
||||
Assert(real != nullptr);
|
||||
@@ -2581,9 +2610,7 @@ void GroupCall::fillActiveVideoEndpoints() {
|
||||
const auto me = real->participantByPeer(_joinAs);
|
||||
if (me && me->videoJoined) {
|
||||
_videoIsWorking = true;
|
||||
_hasNotShownVideo = false;
|
||||
} else {
|
||||
refreshHasNotShownVideo();
|
||||
_videoIsWorking = false;
|
||||
toggleVideo(false);
|
||||
toggleScreenSharing(std::nullopt);
|
||||
@@ -2611,30 +2638,28 @@ void GroupCall::fillActiveVideoEndpoints() {
|
||||
}
|
||||
};
|
||||
using Type = VideoEndpointType;
|
||||
if (_videoIsWorking.current()) {
|
||||
for (const auto &participant : real->participants()) {
|
||||
const auto camera = GetCameraEndpoint(participant.videoParams);
|
||||
if (camera != _cameraEndpoint
|
||||
&& camera != _screenEndpoint
|
||||
&& participant.peer != _joinAs) {
|
||||
const auto paused = IsCameraPaused(participant.videoParams);
|
||||
feedOne({ Type::Camera, participant.peer, camera }, paused);
|
||||
}
|
||||
const auto screen = GetScreenEndpoint(participant.videoParams);
|
||||
if (screen != _cameraEndpoint
|
||||
&& screen != _screenEndpoint
|
||||
&& participant.peer != _joinAs) {
|
||||
const auto paused = IsScreenPaused(participant.videoParams);
|
||||
feedOne({ Type::Screen, participant.peer, screen }, paused);
|
||||
}
|
||||
for (const auto &participant : real->participants()) {
|
||||
const auto camera = GetCameraEndpoint(participant.videoParams);
|
||||
if (camera != _cameraEndpoint
|
||||
&& camera != _screenEndpoint
|
||||
&& participant.peer != _joinAs) {
|
||||
const auto paused = IsCameraPaused(participant.videoParams);
|
||||
feedOne({ Type::Camera, participant.peer, camera }, paused);
|
||||
}
|
||||
const auto screen = GetScreenEndpoint(participant.videoParams);
|
||||
if (screen != _cameraEndpoint
|
||||
&& screen != _screenEndpoint
|
||||
&& participant.peer != _joinAs) {
|
||||
const auto paused = IsScreenPaused(participant.videoParams);
|
||||
feedOne({ Type::Screen, participant.peer, screen }, paused);
|
||||
}
|
||||
feedOne(
|
||||
{ Type::Camera, _joinAs, cameraSharingEndpoint() },
|
||||
isCameraPaused());
|
||||
feedOne(
|
||||
{ Type::Screen, _joinAs, screenSharingEndpoint() },
|
||||
isScreenPaused());
|
||||
}
|
||||
feedOne(
|
||||
{ Type::Camera, _joinAs, cameraSharingEndpoint() },
|
||||
isCameraPaused());
|
||||
feedOne(
|
||||
{ Type::Screen, _joinAs, screenSharingEndpoint() },
|
||||
isScreenPaused());
|
||||
if (large && !largeFound) {
|
||||
setVideoEndpointLarge({});
|
||||
}
|
||||
|
||||
@@ -362,12 +362,6 @@ public:
|
||||
[[nodiscard]] rpl::producer<bool> videoIsWorkingValue() const {
|
||||
return _videoIsWorking.value();
|
||||
}
|
||||
[[nodiscard]] bool hasNotShownVideo() const {
|
||||
return _hasNotShownVideo.current();
|
||||
}
|
||||
[[nodiscard]] rpl::producer<bool> hasNotShownVideoValue() const {
|
||||
return _hasNotShownVideo.value();
|
||||
}
|
||||
|
||||
void setCurrentAudioDevice(bool input, const QString &deviceId);
|
||||
[[nodiscard]] bool isSharingScreen() const;
|
||||
@@ -521,7 +515,6 @@ private:
|
||||
void updateRequestedVideoChannels();
|
||||
void updateRequestedVideoChannelsDelayed();
|
||||
void fillActiveVideoEndpoints();
|
||||
void refreshHasNotShownVideo();
|
||||
|
||||
void editParticipant(
|
||||
not_null<PeerData*> participantPeer,
|
||||
@@ -580,7 +573,6 @@ private:
|
||||
rpl::variable<MuteState> _muted = MuteState::Muted;
|
||||
rpl::variable<bool> _canManage = false;
|
||||
rpl::variable<bool> _videoIsWorking = false;
|
||||
rpl::variable<bool> _hasNotShownVideo = false;
|
||||
bool _initialMuteStateSent = false;
|
||||
bool _acceptFields = false;
|
||||
|
||||
|
||||
@@ -46,125 +46,6 @@ constexpr auto kUserpicBlurRadius = 8;
|
||||
|
||||
using Row = MembersRow;
|
||||
|
||||
[[nodiscard]] int VideoParticipantsLimit(not_null<Main::Session*> session) {
|
||||
return int(session->account().appConfig().get<double>(
|
||||
"groupcall_video_participants_max",
|
||||
30.));
|
||||
}
|
||||
|
||||
void SetupVideoPlaceholder(
|
||||
not_null<Ui::RpWidget*> widget,
|
||||
not_null<PeerData*> chat,
|
||||
int limit) {
|
||||
struct State {
|
||||
QImage blurred;
|
||||
QImage rounded;
|
||||
InMemoryKey key = {};
|
||||
std::shared_ptr<Data::CloudImageView> view;
|
||||
qint64 blurredCacheKey = 0;
|
||||
};
|
||||
const auto state = widget->lifetime().make_state<State>();
|
||||
const auto refreshBlurred = [=] {
|
||||
const auto key = chat->userpicUniqueKey(state->view);
|
||||
if (state->key == key && !state->blurred.isNull()) {
|
||||
return;
|
||||
}
|
||||
constexpr auto size = kUserpicSizeForBlur;
|
||||
state->key = key;
|
||||
state->blurred = QImage(
|
||||
QSize(size, size),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
{
|
||||
auto p = Painter(&state->blurred);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
chat->paintUserpicSquare(p, state->view, 0, 0, size);
|
||||
}
|
||||
state->blurred = Images::BlurLargeImage(
|
||||
std::move(state->blurred),
|
||||
kUserpicBlurRadius);
|
||||
widget->update();
|
||||
};
|
||||
const auto refreshRounded = [=](QSize size) {
|
||||
refreshBlurred();
|
||||
const auto key = state->blurred.cacheKey();
|
||||
if (state->rounded.size() == size && state->blurredCacheKey == key) {
|
||||
return;
|
||||
}
|
||||
state->blurredCacheKey = key;
|
||||
state->rounded = Images::prepare(
|
||||
state->blurred,
|
||||
size.width(),
|
||||
size.width(), // Square
|
||||
Images::Option::Smooth,
|
||||
size.width(),
|
||||
size.height());
|
||||
{
|
||||
auto p = QPainter(&state->rounded);
|
||||
p.fillRect(
|
||||
0,
|
||||
0,
|
||||
size.width(),
|
||||
size.height(),
|
||||
QColor(0, 0, 0, Viewport::kShadowMaxAlpha));
|
||||
}
|
||||
state->rounded = Images::prepare(
|
||||
std::move(state->rounded),
|
||||
size.width(),
|
||||
size.height(),
|
||||
(Images::Option::RoundedLarge | Images::Option::RoundedAll),
|
||||
size.width(),
|
||||
size.height());
|
||||
};
|
||||
chat->loadUserpic();
|
||||
refreshBlurred();
|
||||
|
||||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto size = QSize(
|
||||
widget->width(),
|
||||
widget->height() - st::groupCallVideoSmallSkip);
|
||||
refreshRounded(size * cIntRetinaFactor());
|
||||
|
||||
auto p = QPainter(widget);
|
||||
const auto inner = QRect(QPoint(), size);
|
||||
p.drawImage(inner, state->rounded);
|
||||
st::groupCallPaused.paint(
|
||||
p,
|
||||
(size.width() - st::groupCallPaused.width()) / 2,
|
||||
st::groupCallVideoPlaceholderIconTop,
|
||||
size.width());
|
||||
|
||||
const auto skip = st::groupCallVideoLargeSkip;
|
||||
p.setPen(st::groupCallVideoTextFg);
|
||||
const auto text = QRect(
|
||||
skip,
|
||||
st::groupCallVideoPlaceholderTextTop,
|
||||
(size.width() - 2 * skip),
|
||||
size.height() - st::groupCallVideoPlaceholderTextTop);
|
||||
p.setFont(st::semiboldFont);
|
||||
p.drawText(
|
||||
text,
|
||||
tr::lng_group_call_limit(tr::now, lt_count, int(limit)),
|
||||
style::al_top);
|
||||
}, widget->lifetime());
|
||||
}
|
||||
|
||||
void SetupVideoAboutLimit(
|
||||
not_null<Ui::RpWidget*> widget,
|
||||
not_null<Main::Session*> session,
|
||||
int limit) {
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
widget.get(),
|
||||
tr::lng_group_call_over_limit(lt_count, rpl::single(limit * 1.)),
|
||||
st::groupCallVideoLimitLabel);
|
||||
widget->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
label->resizeToWidth(width);
|
||||
label->moveToLeft(0, st::normalFont->height / 3);
|
||||
widget->resize(width, label->height() + st::normalFont->height);
|
||||
}, label->lifetime());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class Members::Controller final
|
||||
@@ -1658,8 +1539,6 @@ Members::Members(
|
||||
, _layout(_scroll->setOwnedWidget(
|
||||
object_ptr<Ui::VerticalLayout>(_scroll.data())))
|
||||
, _videoWrap(_layout->add(object_ptr<Ui::RpWidget>(_layout.get())))
|
||||
, _videoPlaceholder(std::make_unique<Ui::RpWidget>(_videoWrap.get()))
|
||||
, _videoAboutLimit(std::make_unique<Ui::RpWidget>(_videoWrap.get()))
|
||||
, _viewport(
|
||||
std::make_unique<Viewport>(
|
||||
_videoWrap.get(),
|
||||
@@ -1897,61 +1776,9 @@ void Members::trackViewportGeometry() {
|
||||
_scroll->scrollTopValue(
|
||||
) | rpl::skip(1) | rpl::start_with_next(move, _viewport->lifetime());
|
||||
|
||||
const auto videoLimit = VideoParticipantsLimit(&_call->peer()->session());
|
||||
rpl::combine(
|
||||
_layout->widthValue(),
|
||||
_call->hasNotShownVideoValue()
|
||||
) | rpl::start_with_next([=](int width, bool has) {
|
||||
const auto height = has ? st::groupCallVideoPlaceholderHeight : 0;
|
||||
_videoPlaceholder->setGeometry(0, 0, width, height);
|
||||
}, _videoPlaceholder->lifetime());
|
||||
|
||||
SetupVideoPlaceholder(_videoPlaceholder.get(), _call->peer(), videoLimit);
|
||||
|
||||
_layout->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
_videoAboutLimit->resizeToWidth(width);
|
||||
}, _videoAboutLimit->lifetime());
|
||||
|
||||
using namespace rpl::mappers;
|
||||
auto aboutLimitRelevant = fullCountValue(
|
||||
) | rpl::map(
|
||||
_1 > videoLimit
|
||||
) | rpl::distinct_until_changed();
|
||||
auto aboutLimitShown = rpl::combine(
|
||||
std::move(aboutLimitRelevant),
|
||||
_call->canManageValue(),
|
||||
_1 && _2);
|
||||
|
||||
SetupVideoAboutLimit(
|
||||
_videoAboutLimit.get(),
|
||||
&_call->peer()->session(),
|
||||
videoLimit);
|
||||
|
||||
rpl::combine(
|
||||
_videoPlaceholder->heightValue(),
|
||||
_viewport->fullHeightValue(),
|
||||
_videoAboutLimit->heightValue(),
|
||||
std::move(aboutLimitShown)
|
||||
) | rpl::start_with_next([=](
|
||||
int placeholder,
|
||||
int viewport,
|
||||
int aboutLimit,
|
||||
bool aboutLimitShown) {
|
||||
if (placeholder > 0 || viewport <= 0 || !aboutLimitShown) {
|
||||
aboutLimitShown = false;
|
||||
}
|
||||
|
||||
// This call may update _videoAboutLimit->height() :(
|
||||
_videoAboutLimit->setVisible(aboutLimitShown);
|
||||
|
||||
_videoAboutLimit->move(0, viewport);
|
||||
_videoWrap->resize(
|
||||
_videoWrap->width(),
|
||||
std::max(
|
||||
placeholder,
|
||||
(viewport
|
||||
+ (aboutLimitShown ? _videoAboutLimit->height() : 0))));
|
||||
_viewport->fullHeightValue(
|
||||
) | rpl::start_with_next([=](int viewport) {
|
||||
_videoWrap->resize(_videoWrap->width(), viewport);
|
||||
if (viewport > 0) {
|
||||
move();
|
||||
resize();
|
||||
|
||||
@@ -101,8 +101,6 @@ private:
|
||||
std::unique_ptr<Controller> _listController;
|
||||
not_null<Ui::VerticalLayout*> _layout;
|
||||
const not_null<Ui::RpWidget*> _videoWrap;
|
||||
const std::unique_ptr<Ui::RpWidget> _videoPlaceholder;
|
||||
const std::unique_ptr<Ui::RpWidget> _videoAboutLimit;
|
||||
std::unique_ptr<Viewport> _viewport;
|
||||
rpl::variable<Ui::RpWidget*> _addMemberButton = nullptr;
|
||||
RpWidget *_topSkip = nullptr;
|
||||
|
||||
@@ -37,8 +37,11 @@ void EditGroupCallTitleBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
const QString &placeholder,
|
||||
const QString &title,
|
||||
bool livestream,
|
||||
Fn<void(QString)> done) {
|
||||
box->setTitle(tr::lng_group_call_edit_title());
|
||||
box->setTitle(livestream
|
||||
? tr::lng_group_call_edit_title_channel()
|
||||
: tr::lng_group_call_edit_title());
|
||||
const auto input = box->addRow(object_ptr<Ui::InputField>(
|
||||
box,
|
||||
st::groupCallField,
|
||||
@@ -492,25 +495,36 @@ void LeaveBox(
|
||||
not_null<GroupCall*> call,
|
||||
bool discardChecked,
|
||||
BoxContext context) {
|
||||
const auto livestream = call->peer()->isBroadcast();
|
||||
const auto scheduled = (call->scheduleDate() != 0);
|
||||
if (!scheduled) {
|
||||
box->setTitle(tr::lng_group_call_leave_title());
|
||||
box->setTitle(livestream
|
||||
? tr::lng_group_call_leave_title_channel()
|
||||
: tr::lng_group_call_leave_title());
|
||||
}
|
||||
const auto inCall = (context == BoxContext::GroupCallPanel);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box.get(),
|
||||
(scheduled
|
||||
? tr::lng_group_call_close_sure()
|
||||
: tr::lng_group_call_leave_sure()),
|
||||
? (livestream
|
||||
? tr::lng_group_call_close_sure_channel()
|
||||
: tr::lng_group_call_close_sure())
|
||||
: (livestream
|
||||
? tr::lng_group_call_leave_sure_channel()
|
||||
: 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(),
|
||||
(scheduled
|
||||
? tr::lng_group_call_also_cancel()
|
||||
: tr::lng_group_call_also_end()),
|
||||
? (livestream
|
||||
? tr::lng_group_call_also_cancel_channel()
|
||||
: tr::lng_group_call_also_cancel())
|
||||
: (livestream
|
||||
? tr::lng_group_call_also_end_channel()
|
||||
: tr::lng_group_call_also_end())),
|
||||
discardChecked,
|
||||
(inCall ? st::groupCallCheckbox : st::defaultBoxCheckbox),
|
||||
(inCall ? st::groupCallCheck : st::defaultCheck)),
|
||||
@@ -592,7 +606,11 @@ void FillMenu(
|
||||
menu->addSeparator();
|
||||
}
|
||||
if (addEditTitle) {
|
||||
menu->addAction(tr::lng_group_call_edit_title(tr::now), [=] {
|
||||
const auto livestream = call->peer()->isBroadcast();
|
||||
const auto text = (livestream
|
||||
? tr::lng_group_call_edit_title_channel
|
||||
: tr::lng_group_call_edit_title)(tr::now);
|
||||
menu->addAction(text, [=] {
|
||||
const auto done = [=](const QString &title) {
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->changeTitle(title);
|
||||
@@ -603,6 +621,7 @@ void FillMenu(
|
||||
EditGroupCallTitleBox,
|
||||
peer->name,
|
||||
real->title(),
|
||||
livestream,
|
||||
done));
|
||||
}
|
||||
});
|
||||
@@ -666,15 +685,18 @@ void FillMenu(
|
||||
BoxContext::GroupCallPanel));
|
||||
}
|
||||
};
|
||||
const auto livestream = real->peer()->isBroadcast();
|
||||
menu->addAction(MakeAttentionAction(
|
||||
menu->menu(),
|
||||
(real->scheduleDate()
|
||||
? (call->canManage()
|
||||
? tr::lng_group_call_cancel(tr::now)
|
||||
: tr::lng_group_call_leave(tr::now))
|
||||
: (call->canManage()
|
||||
? tr::lng_group_call_end(tr::now)
|
||||
: tr::lng_group_call_leave(tr::now))),
|
||||
(!call->canManage()
|
||||
? tr::lng_group_call_leave
|
||||
: real->scheduleDate()
|
||||
? (livestream
|
||||
? tr::lng_group_call_cancel_channel
|
||||
: tr::lng_group_call_cancel)
|
||||
: (livestream
|
||||
? tr::lng_group_call_end_channel
|
||||
: tr::lng_group_call_end))(tr::now),
|
||||
finish));
|
||||
}
|
||||
|
||||
|
||||
@@ -335,7 +335,9 @@ void Panel::startScheduledNow() {
|
||||
_call->startScheduledNow();
|
||||
};
|
||||
auto owned = ConfirmBox({
|
||||
.text = { tr::lng_group_call_start_now_sure(tr::now) },
|
||||
.text = { (_call->peer()->isBroadcast()
|
||||
? tr::lng_group_call_start_now_sure_channel
|
||||
: tr::lng_group_call_start_now_sure)(tr::now) },
|
||||
.button = tr::lng_group_call_start_now(),
|
||||
.callback = done,
|
||||
});
|
||||
@@ -973,6 +975,7 @@ void Panel::updateWideControlsVisibility() {
|
||||
}
|
||||
|
||||
void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
||||
const auto livestream = real->peer()->isBroadcast();
|
||||
const auto validateRecordingMark = [=](bool recording) {
|
||||
if (!recording && _recordingMark) {
|
||||
_recordingMark.destroy();
|
||||
@@ -989,7 +992,9 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
||||
const auto skip = st::groupCallRecordingMarkSkip;
|
||||
_recordingMark->resize(size + 2 * skip, size + 2 * skip);
|
||||
_recordingMark->setClickedCallback([=] {
|
||||
showToast({ tr::lng_group_call_is_recorded(tr::now) });
|
||||
showToast({ (livestream
|
||||
? tr::lng_group_call_is_recorded_channel
|
||||
: tr::lng_group_call_is_recorded)(tr::now) });
|
||||
});
|
||||
const auto animate = [=] {
|
||||
const auto opaque = state->opaque;
|
||||
@@ -1024,12 +1029,17 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
|
||||
_1 != 0
|
||||
) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](bool recorded) {
|
||||
const auto livestream = _call->peer()->isBroadcast();
|
||||
validateRecordingMark(recorded);
|
||||
showToast((recorded
|
||||
? tr::lng_group_call_recording_started
|
||||
? (livestream
|
||||
? tr::lng_group_call_recording_started_channel
|
||||
: tr::lng_group_call_recording_started)
|
||||
: _call->recordingStoppedByMe()
|
||||
? tr::lng_group_call_recording_saved
|
||||
: tr::lng_group_call_recording_stopped)(
|
||||
: (livestream
|
||||
? tr::lng_group_call_recording_stopped_channel
|
||||
: tr::lng_group_call_recording_stopped))(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue));
|
||||
}, lifetime());
|
||||
@@ -1277,16 +1287,18 @@ void Panel::kickParticipant(not_null<PeerData*> participantPeer) {
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box.get(),
|
||||
(!participantPeer->isUser()
|
||||
? tr::lng_group_call_remove_channel(
|
||||
tr::now,
|
||||
lt_channel,
|
||||
participantPeer->name)
|
||||
? (_peer->isBroadcast()
|
||||
? tr::lng_group_call_remove_channel_from_channel
|
||||
: tr::lng_group_call_remove_channel)(
|
||||
tr::now,
|
||||
lt_channel,
|
||||
participantPeer->name)
|
||||
: (_peer->isBroadcast()
|
||||
? tr::lng_profile_sure_kick_channel
|
||||
: tr::lng_profile_sure_kick)(
|
||||
tr::now,
|
||||
lt_user,
|
||||
participantPeer->asUser()->firstName)),
|
||||
tr::now,
|
||||
lt_user,
|
||||
participantPeer->asUser()->firstName)),
|
||||
st::groupCallBoxLabel),
|
||||
style::margins(
|
||||
st::boxRowPadding.left(),
|
||||
|
||||
@@ -49,7 +49,9 @@ void Toasts::setupJoinAsChanged() {
|
||||
return (state == State::Joined);
|
||||
}) | rpl::take(1);
|
||||
}) | rpl::flatten_latest() | rpl::start_with_next([=] {
|
||||
_panel->showToast(tr::lng_group_call_join_as_changed(
|
||||
_panel->showToast((_call->peer()->isBroadcast()
|
||||
? tr::lng_group_call_join_as_changed_channel
|
||||
: tr::lng_group_call_join_as_changed)(
|
||||
tr::now,
|
||||
lt_name,
|
||||
Ui::Text::Bold(_call->joinAs()->name),
|
||||
@@ -67,7 +69,9 @@ void Toasts::setupTitleChanged() {
|
||||
? peer->name
|
||||
: peer->groupCall()->title();
|
||||
}) | rpl::start_with_next([=](const QString &title) {
|
||||
_panel->showToast(tr::lng_group_call_title_changed(
|
||||
_panel->showToast((_call->peer()->isBroadcast()
|
||||
? tr::lng_group_call_title_changed_channel
|
||||
: tr::lng_group_call_title_changed)(
|
||||
tr::now,
|
||||
lt_title,
|
||||
Ui::Text::Bold(title),
|
||||
|
||||
@@ -199,6 +199,11 @@ auto FieldAutocomplete::stickerChosen() const
|
||||
return _inner->stickerChosen();
|
||||
}
|
||||
|
||||
auto FieldAutocomplete::choosingProcesses() const
|
||||
-> rpl::producer<FieldAutocomplete::Type> {
|
||||
return _scroll->scrollTopChanges() | rpl::map([=] { return _type; });
|
||||
}
|
||||
|
||||
FieldAutocomplete::~FieldAutocomplete() = default;
|
||||
|
||||
void FieldAutocomplete::paintEvent(QPaintEvent *e) {
|
||||
|
||||
@@ -87,6 +87,12 @@ public:
|
||||
Api::SendOptions options;
|
||||
ChooseMethod method;
|
||||
};
|
||||
enum class Type {
|
||||
Mentions,
|
||||
Hashtags,
|
||||
BotCommands,
|
||||
Stickers,
|
||||
};
|
||||
|
||||
bool chooseSelected(ChooseMethod method) const;
|
||||
|
||||
@@ -111,6 +117,7 @@ public:
|
||||
rpl::producer<HashtagChosen> hashtagChosen() const;
|
||||
rpl::producer<BotCommandChosen> botCommandChosen() const;
|
||||
rpl::producer<StickerChosen> stickerChosen() const;
|
||||
rpl::producer<Type> choosingProcesses() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void showAnimated();
|
||||
@@ -176,12 +183,6 @@ private:
|
||||
ChannelData *_channel = nullptr;
|
||||
EmojiPtr _emoji;
|
||||
uint64 _stickersSeed = 0;
|
||||
enum class Type {
|
||||
Mentions,
|
||||
Hashtags,
|
||||
BotCommands,
|
||||
Stickers,
|
||||
};
|
||||
Type _type = Type::Mentions;
|
||||
QString _filter;
|
||||
QRect _boundings;
|
||||
|
||||
@@ -970,6 +970,11 @@ StickersListWidget::StickersListWidget(
|
||||
}
|
||||
refreshRecent();
|
||||
}, lifetime());
|
||||
|
||||
positionValue(
|
||||
) | rpl::skip(1) | rpl::map_to(
|
||||
TabbedSelector::Action::Update
|
||||
) | rpl::start_to_stream(_choosingUpdated, lifetime());
|
||||
}
|
||||
|
||||
Main::Session &StickersListWidget::session() const {
|
||||
@@ -988,6 +993,11 @@ rpl::producer<> StickersListWidget::checkForHide() const {
|
||||
return _checkForHide.events();
|
||||
}
|
||||
|
||||
auto StickersListWidget::choosingUpdated() const
|
||||
-> rpl::producer<TabbedSelector::Action> {
|
||||
return _choosingUpdated.events();
|
||||
}
|
||||
|
||||
object_ptr<TabbedSelector::InnerFooter> StickersListWidget::createFooter() {
|
||||
Expects(_footer == nullptr);
|
||||
|
||||
@@ -2361,6 +2371,7 @@ TabbedSelector::InnerFooter *StickersListWidget::getFooter() const {
|
||||
}
|
||||
|
||||
void StickersListWidget::processHideFinished() {
|
||||
_choosingUpdated.fire(TabbedSelector::Action::Cancel);
|
||||
clearSelection();
|
||||
clearHeavyData();
|
||||
if (_footer) {
|
||||
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
rpl::producer<TabbedSelector::FileChosen> chosen() const;
|
||||
rpl::producer<> scrollUpdated() const;
|
||||
rpl::producer<> checkForHide() const;
|
||||
rpl::producer<TabbedSelector::Action> choosingUpdated() const;
|
||||
|
||||
void refreshRecent() override;
|
||||
void preloadImages() override;
|
||||
@@ -392,6 +393,7 @@ private:
|
||||
rpl::event_stream<TabbedSelector::FileChosen> _chosen;
|
||||
rpl::event_stream<> _scrollUpdated;
|
||||
rpl::event_stream<> _checkForHide;
|
||||
rpl::event_stream<TabbedSelector::Action> _choosingUpdated;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ object_ptr<Window::SectionWidget> TabbedMemento::createWidget(
|
||||
TabbedSection::TabbedSection(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: Window::SectionWidget(parent, controller, PaintedBackground::Custom)
|
||||
: Window::SectionWidget(parent, controller)
|
||||
, _selector(controller->tabbedSelector()) {
|
||||
_selector->setParent(this);
|
||||
_selector->setRoundRadius(0);
|
||||
|
||||
@@ -479,6 +479,13 @@ auto TabbedSelector::inlineResultChosen() const
|
||||
return hasGifsTab() ? gifs()->inlineResultChosen() : nullptr;
|
||||
}
|
||||
|
||||
auto TabbedSelector::choosingStickerUpdated() const
|
||||
-> rpl::producer<TabbedSelector::Action>{
|
||||
return hasStickersTab()
|
||||
? stickers()->choosingUpdated()
|
||||
: rpl::never<Action>();
|
||||
}
|
||||
|
||||
rpl::producer<> TabbedSelector::cancelled() const {
|
||||
return hasGifsTab() ? gifs()->cancelRequests() : nullptr;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,10 @@ public:
|
||||
EmojiOnly,
|
||||
MediaEditor,
|
||||
};
|
||||
enum class Action {
|
||||
Update,
|
||||
Cancel,
|
||||
};
|
||||
|
||||
TabbedSelector(
|
||||
QWidget *parent,
|
||||
@@ -85,6 +89,7 @@ public:
|
||||
rpl::producer<> checkForHide() const;
|
||||
rpl::producer<> slideFinished() const;
|
||||
rpl::producer<> contextMenuRequested() const;
|
||||
rpl::producer<Action> choosingStickerUpdated() const;
|
||||
|
||||
void setRoundRadius(int radius);
|
||||
void refreshStickers();
|
||||
|
||||
@@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_updates.h"
|
||||
#include "calls/calls_instance.h"
|
||||
#include "countries/countries_manager.h"
|
||||
#include "lang/lang_file_parser.h"
|
||||
#include "lang/lang_translator.h"
|
||||
#include "lang/lang_cloud_manager.h"
|
||||
@@ -42,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_instance.h"
|
||||
#include "mainwidget.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/crash_reports.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_domain.h"
|
||||
#include "main/main_session.h"
|
||||
@@ -94,6 +96,27 @@ constexpr auto kQuitPreventTimeoutMs = crl::time(1500);
|
||||
constexpr auto kAutoLockTimeoutLateMs = crl::time(3000);
|
||||
constexpr auto kClearEmojiImageSourceTimeout = 10 * crl::time(1000);
|
||||
|
||||
void SetCrashAnnotationsGL() {
|
||||
#ifdef Q_OS_WIN
|
||||
CrashReports::SetAnnotation("OpenGL ANGLE", [] {
|
||||
if (Core::App().settings().disableOpenGL()) {
|
||||
return "Disabled";
|
||||
} else switch (Ui::GL::CurrentANGLE()) {
|
||||
case Ui::GL::ANGLE::Auto: return "Auto";
|
||||
case Ui::GL::ANGLE::D3D11: return "Direct3D 11";
|
||||
case Ui::GL::ANGLE::D3D9: return "Direct3D 9";
|
||||
case Ui::GL::ANGLE::D3D11on12: return "D3D11on12";
|
||||
case Ui::GL::ANGLE::OpenGL: return "OpenGL";
|
||||
}
|
||||
Unexpected("Ui::GL::CurrentANGLE value in SetupANGLE.");
|
||||
}());
|
||||
#else // Q_OS_WIN
|
||||
CrashReports::SetAnnotation(
|
||||
"OpenGL",
|
||||
Core::App().settings().disableOpenGL() ? "Disabled" : "Enabled");
|
||||
#endif // Q_OS_WIN
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Application *Application::Instance = nullptr;
|
||||
@@ -287,6 +310,7 @@ void Application::run() {
|
||||
LOG(("Shortcuts Error: %1").arg(error));
|
||||
}
|
||||
|
||||
SetCrashAnnotationsGL();
|
||||
if (!Platform::IsMac() && Ui::GL::LastCrashCheckFailed()) {
|
||||
showOpenGLCrashNotification();
|
||||
}
|
||||
@@ -297,6 +321,14 @@ void Application::run() {
|
||||
_mediaView->show(std::move(request));
|
||||
}
|
||||
}, _window->lifetime());
|
||||
|
||||
{
|
||||
const auto countries = std::make_shared<Countries::Manager>(
|
||||
_domain.get());
|
||||
countries->lifetime().add([=] {
|
||||
[[maybe_unused]] const auto countriesCopy = countries;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Application::showOpenGLCrashNotification() {
|
||||
|
||||
@@ -101,7 +101,12 @@ std::map<int, const char*> BetaLogs() {
|
||||
|
||||
"- Fix animated chat backgrounds in Saved Messages.\n"
|
||||
|
||||
"- Fix \"Sorry, group is inaccessible\" message in scheduled voice chats.\n",
|
||||
"- Fix \"Sorry, group is inaccessible\" message "
|
||||
"in scheduled voice chats.\n",
|
||||
},
|
||||
{
|
||||
2009013,
|
||||
"- See unread comments count when scrolling discussions in channels."
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -140,7 +140,7 @@ QByteArray Settings::serialize() const {
|
||||
<< qint32(_askDownloadPath ? 1 : 0)
|
||||
<< _downloadPath.current()
|
||||
<< _downloadPathBookmark
|
||||
<< qint32(_voiceMsgPlaybackDoubled ? 1 : 0)
|
||||
<< qint32(0) // Old double voice playback speed.
|
||||
<< qint32(_soundNotify ? 1 : 0)
|
||||
<< qint32(_desktopNotify ? 1 : 0)
|
||||
<< qint32(_flashBounceNotify ? 1 : 0)
|
||||
@@ -219,7 +219,8 @@ QByteArray Settings::serialize() const {
|
||||
<< qint32(_hiddenGroupCallTooltips.value())
|
||||
<< qint32(_disableOpenGL ? 1 : 0)
|
||||
<< _photoEditorBrush
|
||||
<< qint32(_groupCallNoiseSuppression ? 1 : 0);
|
||||
<< qint32(_groupCallNoiseSuppression ? 1 : 0)
|
||||
<< qint32(_voicePlaybackSpeed * 100);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -240,7 +241,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
qint32 askDownloadPath = _askDownloadPath ? 1 : 0;
|
||||
QString downloadPath = _downloadPath.current();
|
||||
QByteArray downloadPathBookmark = _downloadPathBookmark;
|
||||
qint32 voiceMsgPlaybackDoubled = _voiceMsgPlaybackDoubled ? 1 : 0;
|
||||
qint32 oldVoiceMsgPlaybackDoubled = 0;
|
||||
qint32 soundNotify = _soundNotify ? 1 : 0;
|
||||
qint32 desktopNotify = _desktopNotify ? 1 : 0;
|
||||
qint32 flashBounceNotify = _flashBounceNotify ? 1 : 0;
|
||||
@@ -271,6 +272,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
qint32 suggestStickersByEmoji = _suggestStickersByEmoji ? 1 : 0;
|
||||
qint32 spellcheckerEnabled = _spellcheckerEnabled.current() ? 1 : 0;
|
||||
qint32 videoPlaybackSpeed = Core::Settings::SerializePlaybackSpeed(_videoPlaybackSpeed.current());
|
||||
qint32 voicePlaybackSpeed = _voicePlaybackSpeed * 100;
|
||||
QByteArray videoPipGeometry = _videoPipGeometry;
|
||||
qint32 dictionariesEnabledCount = 0;
|
||||
std::vector<int> dictionariesEnabled;
|
||||
@@ -312,7 +314,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
>> askDownloadPath
|
||||
>> downloadPath
|
||||
>> downloadPathBookmark
|
||||
>> voiceMsgPlaybackDoubled
|
||||
>> oldVoiceMsgPlaybackDoubled
|
||||
>> soundNotify
|
||||
>> desktopNotify
|
||||
>> flashBounceNotify
|
||||
@@ -455,6 +457,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
if (!stream.atEnd()) {
|
||||
stream >> groupCallNoiseSuppression;
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
stream >> voicePlaybackSpeed;
|
||||
}
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("App Error: "
|
||||
"Bad data for Core::Settings::constructFromSerialized()"));
|
||||
@@ -471,7 +476,6 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
_askDownloadPath = (askDownloadPath == 1);
|
||||
_downloadPath = downloadPath;
|
||||
_downloadPathBookmark = downloadPathBookmark;
|
||||
_voiceMsgPlaybackDoubled = (voiceMsgPlaybackDoubled == 1);
|
||||
_soundNotify = (soundNotify == 1);
|
||||
_desktopNotify = (desktopNotify == 1);
|
||||
_flashBounceNotify = (flashBounceNotify == 1);
|
||||
@@ -525,6 +529,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
_suggestStickersByEmoji = (suggestStickersByEmoji == 1);
|
||||
_spellcheckerEnabled = (spellcheckerEnabled == 1);
|
||||
_videoPlaybackSpeed = DeserializePlaybackSpeed(videoPlaybackSpeed);
|
||||
_voicePlaybackSpeed = oldVoiceMsgPlaybackDoubled
|
||||
? 2.0
|
||||
: voicePlaybackSpeed / 100.;
|
||||
_videoPipGeometry = (videoPipGeometry);
|
||||
_dictionariesEnabled = std::move(dictionariesEnabled);
|
||||
_autoDownloadDictionaries = (autoDownloadDictionaries == 1);
|
||||
@@ -782,7 +789,6 @@ void Settings::resetOnLastLogout() {
|
||||
_downloadPath = QString();
|
||||
_downloadPathBookmark = QByteArray();
|
||||
|
||||
_voiceMsgPlaybackDoubled = false;
|
||||
_soundNotify = true;
|
||||
_desktopNotify = true;
|
||||
_flashBounceNotify = true;
|
||||
@@ -826,6 +832,7 @@ void Settings::resetOnLastLogout() {
|
||||
_suggestStickersByEmoji = true;
|
||||
_spellcheckerEnabled = true;
|
||||
_videoPlaybackSpeed = 1.;
|
||||
_voicePlaybackSpeed = 1.;
|
||||
//_videoPipGeometry = QByteArray();
|
||||
_dictionariesEnabled = std::vector<int>();
|
||||
_autoDownloadDictionaries = true;
|
||||
|
||||
@@ -146,12 +146,6 @@ public:
|
||||
void setDownloadPathBookmark(const QByteArray &value) {
|
||||
_downloadPathBookmark = value;
|
||||
}
|
||||
[[nodiscard]] bool voiceMsgPlaybackDoubled() const {
|
||||
return _voiceMsgPlaybackDoubled;
|
||||
}
|
||||
void setVoiceMsgPlaybackDoubled(bool value) {
|
||||
_voiceMsgPlaybackDoubled = value;
|
||||
}
|
||||
[[nodiscard]] bool soundNotify() const {
|
||||
return _soundNotify;
|
||||
}
|
||||
@@ -427,6 +421,12 @@ public:
|
||||
void setVideoPlaybackSpeed(float64 speed) {
|
||||
_videoPlaybackSpeed = speed;
|
||||
}
|
||||
[[nodiscard]] float64 voicePlaybackSpeed() const {
|
||||
return _voicePlaybackSpeed;
|
||||
}
|
||||
void setVoicePlaybackSpeed(float64 speed) {
|
||||
_voicePlaybackSpeed = speed;
|
||||
}
|
||||
[[nodiscard]] QByteArray videoPipGeometry() const {
|
||||
return _videoPipGeometry;
|
||||
}
|
||||
@@ -633,7 +633,6 @@ private:
|
||||
bool _askDownloadPath = false;
|
||||
rpl::variable<QString> _downloadPath;
|
||||
QByteArray _downloadPathBookmark;
|
||||
bool _voiceMsgPlaybackDoubled = false;
|
||||
bool _soundNotify = true;
|
||||
bool _desktopNotify = true;
|
||||
bool _flashBounceNotify = true;
|
||||
@@ -670,6 +669,7 @@ private:
|
||||
bool _suggestStickersByEmoji = true;
|
||||
rpl::variable<bool> _spellcheckerEnabled = true;
|
||||
rpl::variable<float64> _videoPlaybackSpeed = 1.;
|
||||
float64 _voicePlaybackSpeed = 1.;
|
||||
QByteArray _videoPipGeometry;
|
||||
rpl::variable<std::vector<int>> _dictionariesEnabled;
|
||||
rpl::variable<bool> _autoDownloadDictionaries = true;
|
||||
|
||||
@@ -270,12 +270,12 @@ std::unique_ptr<Launcher> Launcher::Create(int argc, char *argv[]) {
|
||||
return std::make_unique<Platform::Launcher>(argc, argv);
|
||||
}
|
||||
|
||||
Launcher::Launcher(
|
||||
int argc,
|
||||
char *argv[])
|
||||
Launcher::Launcher(int argc, char *argv[])
|
||||
: _argc(argc)
|
||||
, _argv(argv)
|
||||
, _baseIntegration(_argc, _argv) {
|
||||
crl::toggle_fp_exceptions(true);
|
||||
|
||||
base::Integration::Set(&_baseIntegration);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,7 @@ namespace Core {
|
||||
|
||||
class Launcher {
|
||||
public:
|
||||
Launcher(
|
||||
int argc,
|
||||
char *argv[]);
|
||||
Launcher(int argc, char *argv[]);
|
||||
|
||||
static std::unique_ptr<Launcher> Create(int argc, char *argv[]);
|
||||
|
||||
|
||||
@@ -617,11 +617,3 @@ rpl::producer<> on_main_update_requests() {
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
||||
namespace base {
|
||||
|
||||
void EnterFromEventLoop(FnMut<void()> &&method) {
|
||||
Core::Sandbox::Instance().customEnterFromEventLoop(std::move(method));
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
|
||||
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
|
||||
constexpr auto AppName = "Telegram Desktop"_cs;
|
||||
constexpr auto AppFile = "Telegram"_cs;
|
||||
constexpr auto AppVersion = 2009007;
|
||||
constexpr auto AppVersionStr = "2.9.7";
|
||||
constexpr auto AppBetaVersion = true;
|
||||
constexpr auto AppVersion = 3000001;
|
||||
constexpr auto AppVersionStr = "3.0.1";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
465
Telegram/SourceFiles/countries/countries_instance.cpp
Normal file
465
Telegram/SourceFiles/countries/countries_instance.cpp
Normal file
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "countries/countries_instance.h"
|
||||
|
||||
namespace Countries {
|
||||
namespace {
|
||||
|
||||
auto SingleInstance = CountriesInstance();
|
||||
|
||||
const std::array<Info, 231> FallbackList = { {
|
||||
{ "Andorra", "AD", "", { CallingCodeInfo{ "376", {}, { "XX XX XX" } } }, false },
|
||||
{ "United Arab Emirates", "AE", "", { CallingCodeInfo{ "971", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Afghanistan", "AF", "", { CallingCodeInfo{ "93", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Antigua & Barbuda", "AG", "", { CallingCodeInfo{ "1268", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Anguilla", "AI", "", { CallingCodeInfo{ "1264", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Albania", "AL", "", { CallingCodeInfo{ "355", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Armenia", "AM", "", { CallingCodeInfo{ "374", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Angola", "AO", "", { CallingCodeInfo{ "244", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Argentina", "AR", "", { CallingCodeInfo{ "54", {}, {} } }, false },
|
||||
{ "American Samoa", "AS", "", { CallingCodeInfo{ "1684", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Austria", "AT", "", { CallingCodeInfo{ "43", {}, { "X XXXXXXXX" } } }, false },
|
||||
{ "Australia", "AU", "", { CallingCodeInfo{ "61", {}, { "X XXXX XXXX" } } }, false },
|
||||
{ "Aruba", "AW", "", { CallingCodeInfo{ "297", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Azerbaijan", "AZ", "", { CallingCodeInfo{ "994", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Bosnia & Herzegovina", "BA", "", { CallingCodeInfo{ "387", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Barbados", "BB", "", { CallingCodeInfo{ "1246", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Bangladesh", "BD", "", { CallingCodeInfo{ "880", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Belgium", "BE", "", { CallingCodeInfo{ "32", {}, { "XXX XX XX XX" } } }, false },
|
||||
{ "Burkina Faso", "BF", "", { CallingCodeInfo{ "226", {}, { "XX XX XX XX" } } }, false },
|
||||
{ "Bulgaria", "BG", "", { CallingCodeInfo{ "359", {}, {} } }, false },
|
||||
{ "Bahrain", "BH", "", { CallingCodeInfo{ "973", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Burundi", "BI", "", { CallingCodeInfo{ "257", {}, { "XX XX XXXX" } } }, false },
|
||||
{ "Benin", "BJ", "", { CallingCodeInfo{ "229", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Bermuda", "BM", "", { CallingCodeInfo{ "1441", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Brunei Darussalam", "BN", "", { CallingCodeInfo{ "673", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Bolivia", "BO", "", { CallingCodeInfo{ "591", {}, { "X XXX XXXX" } } }, false },
|
||||
{ "Bonaire, Sint Eustatius & Saba", "BQ", "", { CallingCodeInfo{ "599", {}, {} } }, false },
|
||||
{ "Brazil", "BR", "", { CallingCodeInfo{ "55", {}, { "XX XXXXX XXXX" } } }, false },
|
||||
{ "Bahamas", "BS", "", { CallingCodeInfo{ "1242", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Bhutan", "BT", "", { CallingCodeInfo{ "975", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Botswana", "BW", "", { CallingCodeInfo{ "267", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Belarus", "BY", "", { CallingCodeInfo{ "375", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Belize", "BZ", "", { CallingCodeInfo{ "501", {}, {} } }, false },
|
||||
{ "Canada", "CA", "", { CallingCodeInfo{ "1", { "403" }, { "XXX XXX XXXX" } } }, false },
|
||||
{ "Congo (Dem. Rep.)", "CD", "", { CallingCodeInfo{ "243", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Central African Rep.", "CF", "", { CallingCodeInfo{ "236", {}, { "XX XX XX XX" } } }, false },
|
||||
{ "Congo (Rep.)", "CG", "", { CallingCodeInfo{ "242", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Switzerland", "CH", "", { CallingCodeInfo{ "41", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Côte d'Ivoire", "CI", "", { CallingCodeInfo{ "225", {}, { "XX XX XX XXXX" } } }, false },
|
||||
{ "Cook Islands", "CK", "", { CallingCodeInfo{ "682", {}, {} } }, false },
|
||||
{ "Chile", "CL", "", { CallingCodeInfo{ "56", {}, { "X XXXX XXXX" } } }, false },
|
||||
{ "Cameroon", "CM", "", { CallingCodeInfo{ "237", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "China", "CN", "", { CallingCodeInfo{ "86", {}, { "XXX XXXX XXXX" } } }, false },
|
||||
{ "Colombia", "CO", "", { CallingCodeInfo{ "57", {}, { "XXX XXX XXXX" } } }, false },
|
||||
{ "Costa Rica", "CR", "", { CallingCodeInfo{ "506", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Cuba", "CU", "", { CallingCodeInfo{ "53", {}, { "X XXX XXXX" } } }, false },
|
||||
{ "Cape Verde", "CV", "", { CallingCodeInfo{ "238", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Curaçao", "CW", "", { CallingCodeInfo{ "599", { "9" }, {} } }, false },
|
||||
{ "Cyprus", "CY", "", { CallingCodeInfo{ "357", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Czech Republic", "CZ", "", { CallingCodeInfo{ "420", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Germany", "DE", "", { CallingCodeInfo{ "49", {}, { "XXXX XXXXXXX" } } }, false },
|
||||
{ "Djibouti", "DJ", "", { CallingCodeInfo{ "253", {}, { "XX XX XX XX" } } }, false },
|
||||
{ "Denmark", "DK", "", { CallingCodeInfo{ "45", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Dominica", "DM", "", { CallingCodeInfo{ "1767", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Dominican Rep.", "DO", "", { CallingCodeInfo{ "1809", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Algeria", "DZ", "", { CallingCodeInfo{ "213", {}, { "XXX XX XX XX" } } }, false },
|
||||
{ "Ecuador", "EC", "", { CallingCodeInfo{ "593", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Estonia", "EE", "", { CallingCodeInfo{ "372", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Egypt", "EG", "", { CallingCodeInfo{ "20", {}, { "XX XXXX XXXX" } } }, false },
|
||||
{ "Eritrea", "ER", "", { CallingCodeInfo{ "291", {}, { "X XXX XXX" } } }, false },
|
||||
{ "Spain", "ES", "", { CallingCodeInfo{ "34", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Ethiopia", "ET", "", { CallingCodeInfo{ "251", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Finland", "FI", "", { CallingCodeInfo{ "358", {}, {} } }, false },
|
||||
{ "Fiji", "FJ", "", { CallingCodeInfo{ "679", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Falkland Islands", "FK", "", { CallingCodeInfo{ "500", {}, {} } }, false },
|
||||
{ "Micronesia", "FM", "", { CallingCodeInfo{ "691", {}, {} } }, false },
|
||||
{ "Faroe Islands", "FO", "", { CallingCodeInfo{ "298", {}, { "XXX XXX" } } }, false },
|
||||
{ "France", "FR", "", { CallingCodeInfo{ "33", {}, { "X XX XX XX XX" } } }, false },
|
||||
{ "Gabon", "GA", "", { CallingCodeInfo{ "241", {}, { "X XX XX XX" } } }, false },
|
||||
{ "United Kingdom", "GB", "", { CallingCodeInfo{ "44", {}, { "XXXX XXXXXX" } } }, false },
|
||||
{ "Grenada", "GD", "", { CallingCodeInfo{ "1473", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Georgia", "GE", "", { CallingCodeInfo{ "995", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "French Guiana", "GF", "", { CallingCodeInfo{ "594", {}, {} } }, false },
|
||||
{ "Ghana", "GH", "", { CallingCodeInfo{ "233", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Gibraltar", "GI", "", { CallingCodeInfo{ "350", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Greenland", "GL", "", { CallingCodeInfo{ "299", {}, { "XXX XXX" } } }, false },
|
||||
{ "Gambia", "GM", "", { CallingCodeInfo{ "220", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Guinea", "GN", "", { CallingCodeInfo{ "224", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Guadeloupe", "GP", "", { CallingCodeInfo{ "590", {}, { "XXX XX XX XX" } } }, false },
|
||||
{ "Equatorial Guinea", "GQ", "", { CallingCodeInfo{ "240", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Greece", "GR", "", { CallingCodeInfo{ "30", {}, { "XXX XXX XXXX" } } }, false },
|
||||
{ "Guatemala", "GT", "", { CallingCodeInfo{ "502", {}, { "X XXX XXXX" } } }, false },
|
||||
{ "Guam", "GU", "", { CallingCodeInfo{ "1671", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Guinea-Bissau", "GW", "", { CallingCodeInfo{ "245", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Guyana", "GY", "", { CallingCodeInfo{ "592", {}, {} } }, false },
|
||||
{ "Hong Kong", "HK", "", { CallingCodeInfo{ "852", {}, { "X XXX XXXX" } } }, false },
|
||||
{ "Honduras", "HN", "", { CallingCodeInfo{ "504", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Croatia", "HR", "", { CallingCodeInfo{ "385", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Haiti", "HT", "", { CallingCodeInfo{ "509", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Hungary", "HU", "", { CallingCodeInfo{ "36", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Indonesia", "ID", "", { CallingCodeInfo{ "62", {}, { "XXX XXXXXX" } } }, false },
|
||||
{ "Ireland", "IE", "", { CallingCodeInfo{ "353", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Israel", "IL", "", { CallingCodeInfo{ "972", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "India", "IN", "", { CallingCodeInfo{ "91", {}, { "XXXXX XXXXX" } } }, false },
|
||||
{ "Diego Garcia", "IO", "", { CallingCodeInfo{ "246", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Iraq", "IQ", "", { CallingCodeInfo{ "964", {}, { "XXX XXX XXXX" } } }, false },
|
||||
{ "Iran", "IR", "", { CallingCodeInfo{ "98", {}, { "XXX XXX XXXX" } } }, false },
|
||||
{ "Iceland", "IS", "", { CallingCodeInfo{ "354", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Italy", "IT", "", { CallingCodeInfo{ "39", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Jamaica", "JM", "", { CallingCodeInfo{ "1876", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Jordan", "JO", "", { CallingCodeInfo{ "962", {}, { "X XXXX XXXX" } } }, false },
|
||||
{ "Japan", "JP", "", { CallingCodeInfo{ "81", {}, { "XX XXXX XXXX" } } }, false },
|
||||
{ "Kenya", "KE", "", { CallingCodeInfo{ "254", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Kyrgyzstan", "KG", "", { CallingCodeInfo{ "996", {}, { "XXX XXXXXX" } } }, false },
|
||||
{ "Cambodia", "KH", "", { CallingCodeInfo{ "855", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Kiribati", "KI", "", { CallingCodeInfo{ "686", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Comoros", "KM", "", { CallingCodeInfo{ "269", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Saint Kitts & Nevis", "KN", "", { CallingCodeInfo{ "1869", {}, { "XXX XXXX" } } }, false },
|
||||
{ "North Korea", "KP", "", { CallingCodeInfo{ "850", {}, {} } }, false },
|
||||
{ "South Korea", "KR", "", { CallingCodeInfo{ "82", {}, { "XX XXXX XXX" } } }, false },
|
||||
{ "Kuwait", "KW", "", { CallingCodeInfo{ "965", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Cayman Islands", "KY", "", { CallingCodeInfo{ "1345", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Kazakhstan", "KZ", "", { CallingCodeInfo{ "7", { "6" }, { "XXX XXX XX XX" } } }, false },
|
||||
{ "Laos", "LA", "", { CallingCodeInfo{ "856", {}, { "XX XX XXX XXX" } } }, false },
|
||||
{ "Lebanon", "LB", "", { CallingCodeInfo{ "961", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Saint Lucia", "LC", "", { CallingCodeInfo{ "1758", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Liechtenstein", "LI", "", { CallingCodeInfo{ "423", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Sri Lanka", "LK", "", { CallingCodeInfo{ "94", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Liberia", "LR", "", { CallingCodeInfo{ "231", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Lesotho", "LS", "", { CallingCodeInfo{ "266", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Lithuania", "LT", "", { CallingCodeInfo{ "370", {}, { "XXX XXXXX" } } }, false },
|
||||
{ "Luxembourg", "LU", "", { CallingCodeInfo{ "352", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Latvia", "LV", "", { CallingCodeInfo{ "371", {}, { "XXX XXXXX" } } }, false },
|
||||
{ "Libya", "LY", "", { CallingCodeInfo{ "218", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Morocco", "MA", "", { CallingCodeInfo{ "212", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Monaco", "MC", "", { CallingCodeInfo{ "377", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Moldova", "MD", "", { CallingCodeInfo{ "373", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Montenegro", "ME", "", { CallingCodeInfo{ "382", {}, {} } }, false },
|
||||
{ "Madagascar", "MG", "", { CallingCodeInfo{ "261", {}, { "XX XX XXX XX" } } }, false },
|
||||
{ "Marshall Islands", "MH", "", { CallingCodeInfo{ "692", {}, {} } }, false },
|
||||
{ "North Macedonia", "MK", "", { CallingCodeInfo{ "389", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Mali", "ML", "", { CallingCodeInfo{ "223", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Myanmar", "MM", "", { CallingCodeInfo{ "95", {}, {} } }, false },
|
||||
{ "Mongolia", "MN", "", { CallingCodeInfo{ "976", {}, { "XX XX XXXX" } } }, false },
|
||||
{ "Macau", "MO", "", { CallingCodeInfo{ "853", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Northern Mariana Islands", "MP", "", { CallingCodeInfo{ "1670", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Martinique", "MQ", "", { CallingCodeInfo{ "596", {}, {} } }, false },
|
||||
{ "Mauritania", "MR", "", { CallingCodeInfo{ "222", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Montserrat", "MS", "", { CallingCodeInfo{ "1664", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Malta", "MT", "", { CallingCodeInfo{ "356", {}, { "XX XX XX XX" } } }, false },
|
||||
{ "Mauritius", "MU", "", { CallingCodeInfo{ "230", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Maldives", "MV", "", { CallingCodeInfo{ "960", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Malawi", "MW", "", { CallingCodeInfo{ "265", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Mexico", "MX", "", { CallingCodeInfo{ "52", {}, {} } }, false },
|
||||
{ "Malaysia", "MY", "", { CallingCodeInfo{ "60", {}, { "XX XXXX XXXX" } } }, false },
|
||||
{ "Mozambique", "MZ", "", { CallingCodeInfo{ "258", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Namibia", "NA", "", { CallingCodeInfo{ "264", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "New Caledonia", "NC", "", { CallingCodeInfo{ "687", {}, {} } }, false },
|
||||
{ "Niger", "NE", "", { CallingCodeInfo{ "227", {}, { "XX XX XX XX" } } }, false },
|
||||
{ "Norfolk Island", "NF", "", { CallingCodeInfo{ "672", {}, {} } }, false },
|
||||
{ "Nigeria", "NG", "", { CallingCodeInfo{ "234", {}, { "XX XXXX XXXX" } } }, false },
|
||||
{ "Nicaragua", "NI", "", { CallingCodeInfo{ "505", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Netherlands", "NL", "", { CallingCodeInfo{ "31", {}, { "X XX XX XX XX" } } }, false },
|
||||
{ "Norway", "NO", "", { CallingCodeInfo{ "47", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Nepal", "NP", "", { CallingCodeInfo{ "977", {}, { "XX XXXX XXXX" } } }, false },
|
||||
{ "Nauru", "NR", "", { CallingCodeInfo{ "674", {}, {} } }, false },
|
||||
{ "Niue", "NU", "", { CallingCodeInfo{ "683", {}, {} } }, false },
|
||||
{ "New Zealand", "NZ", "", { CallingCodeInfo{ "64", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Oman", "OM", "", { CallingCodeInfo{ "968", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Panama", "PA", "", { CallingCodeInfo{ "507", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Peru", "PE", "", { CallingCodeInfo{ "51", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "French Polynesia", "PF", "", { CallingCodeInfo{ "689", {}, {} } }, false },
|
||||
{ "Papua New Guinea", "PG", "", { CallingCodeInfo{ "675", {}, {} } }, false },
|
||||
{ "Philippines", "PH", "", { CallingCodeInfo{ "63", {}, { "XXX XXX XXXX" } } }, false },
|
||||
{ "Pakistan", "PK", "", { CallingCodeInfo{ "92", {}, { "XXX XXX XXXX" } } }, false },
|
||||
{ "Poland", "PL", "", { CallingCodeInfo{ "48", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Saint Pierre & Miquelon", "PM", "", { CallingCodeInfo{ "508", {}, {} } }, false },
|
||||
{ "Puerto Rico", "PR", "", { CallingCodeInfo{ "1787", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Palestine", "PS", "", { CallingCodeInfo{ "970", {}, { "XXX XX XXXX" } } }, false },
|
||||
{ "Portugal", "PT", "", { CallingCodeInfo{ "351", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Palau", "PW", "", { CallingCodeInfo{ "680", {}, {} } }, false },
|
||||
{ "Paraguay", "PY", "", { CallingCodeInfo{ "595", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Qatar", "QA", "", { CallingCodeInfo{ "974", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Réunion", "RE", "", { CallingCodeInfo{ "262", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Romania", "RO", "", { CallingCodeInfo{ "40", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Serbia", "RS", "", { CallingCodeInfo{ "381", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Russian Federation", "RU", "", { CallingCodeInfo{ "7", {}, { "XXX XXX XXXX" } } }, false },
|
||||
{ "Rwanda", "RW", "", { CallingCodeInfo{ "250", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Saudi Arabia", "SA", "", { CallingCodeInfo{ "966", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Solomon Islands", "SB", "", { CallingCodeInfo{ "677", {}, {} } }, false },
|
||||
{ "Seychelles", "SC", "", { CallingCodeInfo{ "248", {}, { "X XX XX XX" } } }, false },
|
||||
{ "Sudan", "SD", "", { CallingCodeInfo{ "249", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Sweden", "SE", "", { CallingCodeInfo{ "46", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Singapore", "SG", "", { CallingCodeInfo{ "65", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Saint Helena", "SH", "", { CallingCodeInfo{ "247", {}, {} } }, false },
|
||||
{ "Slovenia", "SI", "", { CallingCodeInfo{ "386", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Slovakia", "SK", "", { CallingCodeInfo{ "421", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Sierra Leone", "SL", "", { CallingCodeInfo{ "232", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "San Marino", "SM", "", { CallingCodeInfo{ "378", {}, { "XXX XXX XXXX" } } }, false },
|
||||
{ "Senegal", "SN", "", { CallingCodeInfo{ "221", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Somalia", "SO", "", { CallingCodeInfo{ "252", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Suriname", "SR", "", { CallingCodeInfo{ "597", {}, { "XXX XXXX" } } }, false },
|
||||
{ "South Sudan", "SS", "", { CallingCodeInfo{ "211", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "São Tomé & Príncipe", "ST", "", { CallingCodeInfo{ "239", {}, { "XX XXXXX" } } }, false },
|
||||
{ "El Salvador", "SV", "", { CallingCodeInfo{ "503", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Sint Maarten", "SX", "", { CallingCodeInfo{ "1721", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Syria", "SY", "", { CallingCodeInfo{ "963", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Eswatini", "SZ", "", { CallingCodeInfo{ "268", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Turks & Caicos Islands", "TC", "", { CallingCodeInfo{ "1649", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Chad", "TD", "", { CallingCodeInfo{ "235", {}, { "XX XX XX XX" } } }, false },
|
||||
{ "Togo", "TG", "", { CallingCodeInfo{ "228", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Thailand", "TH", "", { CallingCodeInfo{ "66", {}, { "X XXXX XXXX" } } }, false },
|
||||
{ "Tajikistan", "TJ", "", { CallingCodeInfo{ "992", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Tokelau", "TK", "", { CallingCodeInfo{ "690", {}, {} } }, false },
|
||||
{ "Timor-Leste", "TL", "", { CallingCodeInfo{ "670", {}, {} } }, false },
|
||||
{ "Turkmenistan", "TM", "", { CallingCodeInfo{ "993", {}, { "XX XXXXXX" } } }, false },
|
||||
{ "Tunisia", "TN", "", { CallingCodeInfo{ "216", {}, { "XX XXX XXX" } } }, false },
|
||||
{ "Tonga", "TO", "", { CallingCodeInfo{ "676", {}, {} } }, false },
|
||||
{ "Turkey", "TR", "", { CallingCodeInfo{ "90", {}, { "XXX XXX XXXX" } } }, false },
|
||||
{ "Trinidad & Tobago", "TT", "", { CallingCodeInfo{ "1868", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Tuvalu", "TV", "", { CallingCodeInfo{ "688", {}, {} } }, false },
|
||||
{ "Taiwan", "TW", "", { CallingCodeInfo{ "886", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "Tanzania", "TZ", "", { CallingCodeInfo{ "255", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Ukraine", "UA", "", { CallingCodeInfo{ "380", {}, { "XX XXX XX XX" } } }, false },
|
||||
{ "Uganda", "UG", "", { CallingCodeInfo{ "256", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "USA", "US", "United States of America", { CallingCodeInfo{ "1", {}, { "XXX XXX XXXX" } } }, false },
|
||||
{ "Uruguay", "UY", "", { CallingCodeInfo{ "598", {}, { "X XXX XXXX" } } }, false },
|
||||
{ "Uzbekistan", "UZ", "", { CallingCodeInfo{ "998", {}, { "XX XXX XX XX" } } }, false },
|
||||
{ "Saint Vincent & the Grenadines", "VC", "", { CallingCodeInfo{ "1784", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Venezuela", "VE", "", { CallingCodeInfo{ "58", {}, { "XXX XXX XXXX" } } }, false },
|
||||
{ "British Virgin Islands", "VG", "", { CallingCodeInfo{ "1284", {}, { "XXX XXXX" } } }, false },
|
||||
{ "US Virgin Islands", "VI", "", { CallingCodeInfo{ "1340", {}, { "XXX XXXX" } } }, false },
|
||||
{ "Vietnam", "VN", "", { CallingCodeInfo{ "84", {}, {} } }, false },
|
||||
{ "Vanuatu", "VU", "", { CallingCodeInfo{ "678", {}, {} } }, false },
|
||||
{ "Wallis & Futuna", "WF", "", { CallingCodeInfo{ "681", {}, {} } }, false },
|
||||
{ "Samoa", "WS", "", { CallingCodeInfo{ "685", {}, {} } }, false },
|
||||
{ "Kosovo", "XK", "", { CallingCodeInfo{ "383", {}, { "XXXX XXXX" } } }, false },
|
||||
{ "Yemen", "YE", "", { CallingCodeInfo{ "967", {}, { "XXX XXX XXX" } } }, false },
|
||||
{ "South Africa", "ZA", "", { CallingCodeInfo{ "27", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Zambia", "ZM", "", { CallingCodeInfo{ "260", {}, { "XX XXX XXXX" } } }, false },
|
||||
{ "Zimbabwe", "ZW", "", { CallingCodeInfo{ "263", {}, { "XX XXX XXXX" } } }, false },
|
||||
} };
|
||||
|
||||
} // namespace
|
||||
|
||||
CountriesInstance::CountriesInstance() {
|
||||
}
|
||||
|
||||
const std::vector<Info> &CountriesInstance::list() {
|
||||
if (_list.empty()) {
|
||||
_list = (FallbackList | ranges::to_vector);
|
||||
}
|
||||
return _list;
|
||||
}
|
||||
|
||||
void CountriesInstance::setList(std::vector<Info> &&infos) {
|
||||
_list = std::move(infos);
|
||||
_byCode.clear();
|
||||
_byISO2.clear();
|
||||
}
|
||||
|
||||
const CountriesInstance::Map &CountriesInstance::byCode() {
|
||||
if (_byCode.empty()) {
|
||||
_byCode.reserve(list().size());
|
||||
for (const auto &entry : list()) {
|
||||
for (const auto &code : entry.codes) {
|
||||
_byCode.insert(code.callingCode, &entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _byCode;
|
||||
}
|
||||
|
||||
const CountriesInstance::Map &CountriesInstance::byISO2() {
|
||||
if (_byISO2.empty()) {
|
||||
_byISO2.reserve(list().size());
|
||||
for (const auto &entry : list()) {
|
||||
_byISO2.insert(entry.iso2, &entry);
|
||||
}
|
||||
}
|
||||
return _byISO2;
|
||||
}
|
||||
|
||||
QString CountriesInstance::validPhoneCode(QString fullCode) {
|
||||
const auto &listByCode = byCode();
|
||||
while (fullCode.length()) {
|
||||
const auto i = listByCode.constFind(fullCode);
|
||||
if (i != listByCode.cend()) {
|
||||
return fullCode;
|
||||
}
|
||||
fullCode.chop(1);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString CountriesInstance::countryNameByISO2(const QString &iso) {
|
||||
const auto &listByISO2 = byISO2();
|
||||
const auto i = listByISO2.constFind(iso);
|
||||
return (i != listByISO2.cend()) ? (*i)->name : QString();
|
||||
}
|
||||
|
||||
QString CountriesInstance::countryISO2ByPhone(const QString &phone) {
|
||||
const auto &listByCode = byCode();
|
||||
const auto code = validPhoneCode(phone);
|
||||
const auto i = listByCode.find(code);
|
||||
return (i != listByCode.cend()) ? (*i)->iso2 : QString();
|
||||
}
|
||||
|
||||
FormatResult CountriesInstance::format(FormatArgs args) {
|
||||
// Ported from TDLib.
|
||||
if (args.phone.isEmpty()) {
|
||||
return FormatResult();
|
||||
}
|
||||
const auto &phoneNumber = args.phone;
|
||||
|
||||
const Info *bestCountryPtr = nullptr;
|
||||
const CallingCodeInfo *bestCallingCodePtr = nullptr;
|
||||
auto bestLength = size_t(0);
|
||||
[[maybe_unused]] auto isPrefix = false;
|
||||
for (const auto &country : list()) {
|
||||
for (auto &callingCode : country.codes) {
|
||||
if (phoneNumber.startsWith(callingCode.callingCode)) {
|
||||
const auto codeSize = callingCode.callingCode.size();
|
||||
for (const auto &prefix : callingCode.prefixes) {
|
||||
if (prefix.startsWith(phoneNumber.midRef(codeSize))) {
|
||||
isPrefix = true;
|
||||
}
|
||||
if ((codeSize + prefix.size()) > bestLength &&
|
||||
phoneNumber.midRef(codeSize).startsWith(prefix)) {
|
||||
bestCountryPtr = &country;
|
||||
bestCallingCodePtr = &callingCode;
|
||||
bestLength = codeSize + prefix.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (callingCode.callingCode.startsWith(phoneNumber)) {
|
||||
isPrefix = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bestCountryPtr == nullptr) {
|
||||
return FormatResult{ .formatted = phoneNumber };
|
||||
}
|
||||
if (args.onlyCode) {
|
||||
return FormatResult{ .code = bestCallingCodePtr->callingCode };
|
||||
}
|
||||
|
||||
const auto codeSize = int(bestCallingCodePtr->callingCode.size());
|
||||
|
||||
if (args.onlyGroups && args.incomplete) {
|
||||
auto groups = args.skipCode
|
||||
? QVector<int>()
|
||||
: QVector<int>{ codeSize };
|
||||
auto groupSize = 0;
|
||||
if (bestCallingCodePtr->patterns.empty()) {
|
||||
return FormatResult{ .groups = std::move(groups) };
|
||||
}
|
||||
for (const auto &c : bestCallingCodePtr->patterns.front()) {
|
||||
if (c == ' ') {
|
||||
groups.push_back(base::take(groupSize));
|
||||
} else {
|
||||
groupSize++;
|
||||
}
|
||||
}
|
||||
if (groupSize) {
|
||||
groups.push_back(base::take(groupSize));
|
||||
}
|
||||
return FormatResult{ .groups = std::move(groups) };
|
||||
}
|
||||
|
||||
const auto formattedPart = phoneNumber.mid(codeSize);
|
||||
auto formattedResult = formattedPart;
|
||||
auto groups = QVector<int>();
|
||||
auto maxMatchedDigits = size_t(0);
|
||||
for (auto &pattern : bestCallingCodePtr->patterns) {
|
||||
auto resultGroups = QVector<int>();
|
||||
auto result = QString();
|
||||
auto currentPatternPos = int(0);
|
||||
auto isFailedMatch = false;
|
||||
auto matchedDigits = size_t(0);
|
||||
auto groupSize = 0;
|
||||
for (const auto &c : formattedPart) {
|
||||
while ((currentPatternPos < pattern.size())
|
||||
&& (pattern[currentPatternPos] != 'X')
|
||||
&& !pattern[currentPatternPos].isDigit()) {
|
||||
if (args.onlyGroups) {
|
||||
resultGroups.push_back(groupSize);
|
||||
groupSize = 0;
|
||||
} else {
|
||||
result += pattern[currentPatternPos];
|
||||
}
|
||||
currentPatternPos++;
|
||||
}
|
||||
if (!args.onlyGroups && (currentPatternPos == pattern.size())) {
|
||||
result += ' ';
|
||||
}
|
||||
if ((currentPatternPos >= pattern.size())
|
||||
|| (pattern[currentPatternPos] == 'X')) {
|
||||
currentPatternPos++;
|
||||
if (args.onlyGroups) {
|
||||
groupSize++;
|
||||
} else {
|
||||
result += c;
|
||||
}
|
||||
} else {
|
||||
if (c == pattern[currentPatternPos]) {
|
||||
matchedDigits++;
|
||||
currentPatternPos++;
|
||||
if (args.onlyGroups) {
|
||||
groupSize++;
|
||||
} else {
|
||||
result += c;
|
||||
}
|
||||
} else {
|
||||
isFailedMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (groupSize) {
|
||||
resultGroups.push_back(groupSize);
|
||||
}
|
||||
if (!isFailedMatch && matchedDigits >= maxMatchedDigits) {
|
||||
maxMatchedDigits = matchedDigits;
|
||||
if (args.onlyGroups) {
|
||||
groups = std::move(resultGroups);
|
||||
} else {
|
||||
formattedResult = std::move(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!args.skipCode) {
|
||||
if (args.onlyGroups) {
|
||||
groups.push_front(codeSize);
|
||||
} else {
|
||||
formattedResult = '+'
|
||||
+ bestCallingCodePtr->callingCode
|
||||
+ ' '
|
||||
+ std::move(formattedResult);
|
||||
}
|
||||
}
|
||||
|
||||
return FormatResult{
|
||||
.formatted = (args.onlyGroups
|
||||
? QString()
|
||||
: std::move(formattedResult)),
|
||||
.groups = std::move(groups),
|
||||
};
|
||||
}
|
||||
|
||||
CountriesInstance &Instance() {
|
||||
return SingleInstance;
|
||||
}
|
||||
|
||||
QString ExtractPhoneCode(const QString &phone) {
|
||||
return Instance().format({ .phone = phone, .onlyCode = true }).code;
|
||||
}
|
||||
|
||||
} // namespace Countries
|
||||
70
Telegram/SourceFiles/countries/countries_instance.h
Normal file
70
Telegram/SourceFiles/countries/countries_instance.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Countries {
|
||||
|
||||
struct CallingCodeInfo {
|
||||
QString callingCode;
|
||||
std::vector<QString> prefixes;
|
||||
std::vector<QString> patterns;
|
||||
};
|
||||
|
||||
struct Info {
|
||||
QString name;
|
||||
QString iso2;
|
||||
QString alternativeName;
|
||||
std::vector<CallingCodeInfo> codes;
|
||||
bool isHidden = false;
|
||||
};
|
||||
|
||||
struct FormatResult {
|
||||
QString formatted;
|
||||
QVector<int> groups;
|
||||
QString code;
|
||||
};
|
||||
|
||||
struct FormatArgs {
|
||||
QString phone;
|
||||
bool onlyGroups = false;
|
||||
bool skipCode = false;
|
||||
bool incomplete = false;
|
||||
bool onlyCode = false;
|
||||
};
|
||||
|
||||
class CountriesInstance final {
|
||||
public:
|
||||
using Map = QHash<QString, const Info *>;
|
||||
|
||||
CountriesInstance();
|
||||
[[nodiscard]] const std::vector<Info> &list();
|
||||
void setList(std::vector<Info> &&infos);
|
||||
|
||||
[[nodiscard]] const Map &byCode();
|
||||
[[nodiscard]] const Map &byISO2();
|
||||
|
||||
[[nodiscard]] QString validPhoneCode(QString fullCode);
|
||||
[[nodiscard]] QString countryNameByISO2(const QString &iso);
|
||||
[[nodiscard]] QString countryISO2ByPhone(const QString &phone);
|
||||
|
||||
[[nodiscard]] FormatResult format(FormatArgs args);
|
||||
|
||||
private:
|
||||
std::vector<Info> _list;
|
||||
|
||||
Map _byCode;
|
||||
Map _byISO2;
|
||||
|
||||
};
|
||||
|
||||
CountriesInstance &Instance();
|
||||
|
||||
[[nodiscard]] QString ExtractPhoneCode(const QString &phone);
|
||||
|
||||
} // namespace Countries
|
||||
257
Telegram/SourceFiles/countries/countries_manager.cpp
Normal file
257
Telegram/SourceFiles/countries/countries_manager.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "countries/countries_manager.h"
|
||||
|
||||
#include "core/application.h"
|
||||
#include "countries/countries_instance.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_domain.h"
|
||||
#include "mtproto/mtp_instance.h"
|
||||
|
||||
#include <QtCore/QFile>
|
||||
|
||||
namespace Countries {
|
||||
namespace {
|
||||
|
||||
struct FileData {
|
||||
int hash = 0;
|
||||
std::vector<Info> infos;
|
||||
};
|
||||
|
||||
auto ProcessAlternativeName(Info &&info) {
|
||||
if (info.name == u"USA"_q) {
|
||||
info.alternativeName = u"United States of America"_q;
|
||||
}
|
||||
return std::move(info);
|
||||
}
|
||||
|
||||
[[nodiscard]] QByteArray SerializeCodeInfo(const CallingCodeInfo &info) {
|
||||
auto result = QByteArray();
|
||||
auto stream = QDataStream(&result, QIODevice::WriteOnly);
|
||||
stream.setVersion(QDataStream::Qt_5_3);
|
||||
stream
|
||||
<< info.callingCode
|
||||
<< int(info.prefixes.size())
|
||||
<< int(info.patterns.size());
|
||||
for (const auto &prefix : info.prefixes) {
|
||||
stream << prefix;
|
||||
}
|
||||
for (const auto &pattern : info.patterns) {
|
||||
stream << pattern;
|
||||
}
|
||||
stream.device()->close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] CallingCodeInfo DeserializeCodeInfo(const QByteArray &data) {
|
||||
auto stream = QDataStream(data);
|
||||
auto result = CallingCodeInfo();
|
||||
auto prefixesCount = qint32(0);
|
||||
auto patternsCount = qint32(0);
|
||||
stream
|
||||
>> result.callingCode
|
||||
>> prefixesCount
|
||||
>> patternsCount;
|
||||
for (auto i = 0; i < prefixesCount; i++) {
|
||||
auto prefix = QString();
|
||||
stream >> prefix;
|
||||
result.prefixes.push_back(std::move(prefix));
|
||||
}
|
||||
for (auto i = 0; i < patternsCount; i++) {
|
||||
auto pattern = QString();
|
||||
stream >> pattern;
|
||||
result.patterns.push_back(std::move(pattern));
|
||||
}
|
||||
return (stream.status() != QDataStream::Ok)
|
||||
? CallingCodeInfo()
|
||||
: result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QByteArray SerializeInfo(const Info &info) {
|
||||
auto result = QByteArray();
|
||||
auto stream = QDataStream(&result, QIODevice::WriteOnly);
|
||||
stream.setVersion(QDataStream::Qt_5_3);
|
||||
stream
|
||||
<< info.name
|
||||
<< info.iso2
|
||||
<< info.alternativeName
|
||||
<< info.isHidden
|
||||
<< int(info.codes.size());
|
||||
for (const auto &code : info.codes) {
|
||||
stream << SerializeCodeInfo(code);
|
||||
}
|
||||
stream.device()->close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] Info DeserializeInfo(const QByteArray &data) {
|
||||
auto stream = QDataStream(data);
|
||||
auto result = Info();
|
||||
auto codesCount = qint32(0);
|
||||
stream
|
||||
>> result.name
|
||||
>> result.iso2
|
||||
>> result.alternativeName
|
||||
>> result.isHidden
|
||||
>> codesCount;
|
||||
for (auto i = 0; i < codesCount; i++) {
|
||||
auto code = QByteArray();
|
||||
stream >> code;
|
||||
result.codes.push_back(DeserializeCodeInfo(code));
|
||||
}
|
||||
return (stream.status() != QDataStream::Ok)
|
||||
? Info()
|
||||
: result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QByteArray Serialize(const FileData &data) {
|
||||
auto result = QByteArray();
|
||||
auto stream = QDataStream(&result, QIODevice::WriteOnly);
|
||||
stream.setVersion(QDataStream::Qt_5_3);
|
||||
stream
|
||||
<< data.hash
|
||||
<< int(data.infos.size());
|
||||
for (const auto &info : data.infos) {
|
||||
stream << SerializeInfo(info);
|
||||
}
|
||||
stream.device()->close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] FileData Deserialize(const QByteArray &data) {
|
||||
auto stream = QDataStream(data);
|
||||
auto hash = int(0);
|
||||
auto infosCount = qint32(0);
|
||||
auto infos = std::vector<Info>();
|
||||
stream >> hash >> infosCount;
|
||||
for (auto i = 0; i < infosCount; i++) {
|
||||
auto info = QByteArray();
|
||||
stream >> info;
|
||||
infos.push_back(DeserializeInfo(info));
|
||||
}
|
||||
return (stream.status() != QDataStream::Ok)
|
||||
? FileData()
|
||||
: FileData{ .hash = hash, .infos = std::move(infos) };
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Manager::Manager(not_null<Main::Domain*> domain)
|
||||
: _path(cWorkingDir() + "tdata/countries") {
|
||||
read();
|
||||
domain->activeValue(
|
||||
) | rpl::map([=](Main::Account *account) {
|
||||
if (!account) {
|
||||
_api.reset();
|
||||
}
|
||||
return account
|
||||
? account->mtpMainSessionValue()
|
||||
: rpl::never<not_null<MTP::Instance*>>();
|
||||
}) | rpl::flatten_latest(
|
||||
) | rpl::start_with_next([=](not_null<MTP::Instance*> instance) {
|
||||
_api.emplace(instance);
|
||||
request();
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
void Manager::read() {
|
||||
auto file = QFile(_path);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto stream = QDataStream(&file);
|
||||
auto data = QByteArray();
|
||||
stream >> data;
|
||||
auto fileData = Deserialize(data);
|
||||
|
||||
_hash = fileData.hash;
|
||||
Instance().setList(base::take(fileData.infos));
|
||||
}
|
||||
|
||||
void Manager::write() const {
|
||||
auto file = QFile(_path);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto stream = QDataStream(&file);
|
||||
stream << Serialize({ .hash = _hash, .infos = Instance().list() });
|
||||
}
|
||||
|
||||
void Manager::request() {
|
||||
Expects(_api.has_value());
|
||||
|
||||
const auto convertMTP = [](const auto &vector, bool force = false) {
|
||||
if (!vector) {
|
||||
return std::vector<QString>(force ? 1 : 0);
|
||||
}
|
||||
return ranges::views::all(
|
||||
vector->v
|
||||
) | ranges::views::transform([](const MTPstring &s) -> QString {
|
||||
return qs(s);
|
||||
}) | ranges::to_vector;
|
||||
};
|
||||
|
||||
_api->request(MTPhelp_GetCountriesList(
|
||||
MTP_string(),
|
||||
MTP_int(_hash)
|
||||
)).done([=](const MTPhelp_CountriesList &result) {
|
||||
result.match([&](const MTPDhelp_countriesList &data) {
|
||||
_hash = data.vhash().v;
|
||||
|
||||
auto infos = std::vector<Info>();
|
||||
|
||||
for (const auto &country : data.vcountries().v) {
|
||||
|
||||
const auto &countryData = country.c_help_country();
|
||||
if (countryData.is_hidden()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto info = Info(ProcessAlternativeName({
|
||||
.name = countryData.vdefault_name().v,
|
||||
.iso2 = countryData.viso2().v,
|
||||
.isHidden = countryData.is_hidden(),
|
||||
}));
|
||||
for (const auto &code : countryData.vcountry_codes().v) {
|
||||
const auto &codeData = code.c_help_countryCode();
|
||||
info.codes.push_back(CallingCodeInfo{
|
||||
.callingCode = codeData.vcountry_code().v,
|
||||
.prefixes = convertMTP(codeData.vprefixes(), true),
|
||||
.patterns = convertMTP(codeData.vpatterns()),
|
||||
});
|
||||
}
|
||||
|
||||
infos.push_back(std::move(info));
|
||||
}
|
||||
|
||||
Instance().setList(std::move(infos));
|
||||
write();
|
||||
}, [](const MTPDhelp_countriesListNotModified &data) {
|
||||
});
|
||||
_lifetime.destroy();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
LOG(("API Error: getting countries failed with error %1"
|
||||
).arg(error.type()));
|
||||
_lifetime.destroy();
|
||||
}).send();
|
||||
}
|
||||
|
||||
rpl::lifetime &Manager::lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
||||
Manager::~Manager() {
|
||||
}
|
||||
|
||||
} // namespace Countries
|
||||
38
Telegram/SourceFiles/countries/countries_manager.h
Normal file
38
Telegram/SourceFiles/countries/countries_manager.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
namespace Main {
|
||||
class Domain;
|
||||
} // namespace Main
|
||||
|
||||
namespace Countries {
|
||||
|
||||
class Manager final {
|
||||
public:
|
||||
Manager(not_null<Main::Domain*> domain);
|
||||
~Manager();
|
||||
|
||||
void read();
|
||||
void write() const;
|
||||
|
||||
rpl::lifetime &lifetime();
|
||||
|
||||
private:
|
||||
void request();
|
||||
|
||||
std::optional<MTP::Sender> _api;
|
||||
const QString _path;
|
||||
int _hash = 0;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
};
|
||||
|
||||
} // namespace Countries
|
||||
103
Telegram/SourceFiles/data/data_audio_msg_id.cpp
Normal file
103
Telegram/SourceFiles/data/data_audio_msg_id.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_audio_msg_id.h"
|
||||
|
||||
#include "data/data_document.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMinLengthForChangeablePlaybackSpeed = 20 * TimeId(60); // 20 minutes.
|
||||
|
||||
} // namespace
|
||||
|
||||
AudioMsgId::AudioMsgId() {
|
||||
}
|
||||
|
||||
AudioMsgId::AudioMsgId(
|
||||
not_null<DocumentData*> audio,
|
||||
FullMsgId msgId,
|
||||
uint32 externalPlayId)
|
||||
: _audio(audio)
|
||||
, _contextId(msgId)
|
||||
, _externalPlayId(externalPlayId)
|
||||
, _changeablePlaybackSpeed(_audio->isVoiceMessage()
|
||||
|| _audio->isVideoMessage()
|
||||
|| (_audio->getDuration() >= kMinLengthForChangeablePlaybackSpeed)) {
|
||||
setTypeFromAudio();
|
||||
}
|
||||
|
||||
uint32 AudioMsgId::CreateExternalPlayId() {
|
||||
static auto Result = uint32(0);
|
||||
return ++Result ? Result : ++Result;
|
||||
}
|
||||
|
||||
AudioMsgId AudioMsgId::ForVideo() {
|
||||
auto result = AudioMsgId();
|
||||
result._externalPlayId = CreateExternalPlayId();
|
||||
result._type = Type::Video;
|
||||
return result;
|
||||
}
|
||||
|
||||
void AudioMsgId::setTypeFromAudio() {
|
||||
if (_audio->isVoiceMessage() || _audio->isVideoMessage()) {
|
||||
_type = Type::Voice;
|
||||
} else if (_audio->isVideoFile()) {
|
||||
_type = Type::Video;
|
||||
} else if (_audio->isAudioFile()) {
|
||||
_type = Type::Song;
|
||||
} else {
|
||||
_type = Type::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
AudioMsgId::Type AudioMsgId::type() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
DocumentData *AudioMsgId::audio() const {
|
||||
return _audio;
|
||||
}
|
||||
|
||||
FullMsgId AudioMsgId::contextId() const {
|
||||
return _contextId;
|
||||
}
|
||||
|
||||
uint32 AudioMsgId::externalPlayId() const {
|
||||
return _externalPlayId;
|
||||
}
|
||||
|
||||
bool AudioMsgId::changeablePlaybackSpeed() const {
|
||||
return _changeablePlaybackSpeed;
|
||||
}
|
||||
|
||||
AudioMsgId::operator bool() const {
|
||||
return (_audio != nullptr) || (_externalPlayId != 0);
|
||||
}
|
||||
|
||||
bool AudioMsgId::operator<(const AudioMsgId &other) const {
|
||||
if (quintptr(audio()) < quintptr(other.audio())) {
|
||||
return true;
|
||||
} else if (quintptr(other.audio()) < quintptr(audio())) {
|
||||
return false;
|
||||
} else if (contextId() < other.contextId()) {
|
||||
return true;
|
||||
} else if (other.contextId() < contextId()) {
|
||||
return false;
|
||||
}
|
||||
return (externalPlayId() < other.externalPlayId());
|
||||
}
|
||||
|
||||
bool AudioMsgId::operator==(const AudioMsgId &other) const {
|
||||
return (audio() == other.audio())
|
||||
&& (contextId() == other.contextId())
|
||||
&& (externalPlayId() == other.externalPlayId());
|
||||
}
|
||||
|
||||
bool AudioMsgId::operator!=(const AudioMsgId &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
50
Telegram/SourceFiles/data/data_audio_msg_id.h
Normal file
50
Telegram/SourceFiles/data/data_audio_msg_id.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class DocumentData;
|
||||
|
||||
class AudioMsgId final {
|
||||
public:
|
||||
enum class Type {
|
||||
Unknown,
|
||||
Voice,
|
||||
Song,
|
||||
Video,
|
||||
};
|
||||
|
||||
AudioMsgId();
|
||||
AudioMsgId(
|
||||
not_null<DocumentData*> audio,
|
||||
FullMsgId msgId,
|
||||
uint32 externalPlayId = 0);
|
||||
|
||||
[[nodiscard]] static uint32 CreateExternalPlayId();
|
||||
[[nodiscard]] static AudioMsgId ForVideo();
|
||||
|
||||
[[nodiscard]] Type type() const;
|
||||
[[nodiscard]] DocumentData *audio() const;
|
||||
[[nodiscard]] FullMsgId contextId() const;
|
||||
[[nodiscard]] uint32 externalPlayId() const;
|
||||
[[nodiscard]] bool changeablePlaybackSpeed() const;
|
||||
[[nodiscard]] explicit operator bool() const;
|
||||
|
||||
bool operator<(const AudioMsgId &other) const;
|
||||
bool operator==(const AudioMsgId &other) const;
|
||||
bool operator!=(const AudioMsgId &other) const;
|
||||
|
||||
private:
|
||||
void setTypeFromAudio();
|
||||
|
||||
DocumentData *_audio = nullptr;
|
||||
Type _type = Type::Unknown;
|
||||
FullMsgId _contextId;
|
||||
uint32 _externalPlayId = 0;
|
||||
bool _changeablePlaybackSpeed = false;
|
||||
|
||||
};
|
||||
@@ -17,6 +17,10 @@ namespace Dialogs {
|
||||
class Entry;
|
||||
} // namespace Dialogs
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Data {
|
||||
|
||||
namespace details {
|
||||
@@ -57,7 +61,7 @@ struct PeerUpdate {
|
||||
Notifications = (1U << 4),
|
||||
Migration = (1U << 5),
|
||||
UnavailableReason = (1U << 6),
|
||||
PinnedMessages = (1U << 7),
|
||||
ChatThemeEmoji = (1U << 7),
|
||||
IsBlocked = (1U << 8),
|
||||
MessagesTTL = (1U << 9),
|
||||
|
||||
@@ -118,8 +122,9 @@ struct HistoryUpdate {
|
||||
BotKeyboard = (1U << 11),
|
||||
CloudDraft = (1U << 12),
|
||||
LocalDraftSet = (1U << 13),
|
||||
PinnedMessages = (1U << 14),
|
||||
|
||||
LastUsedBit = (1U << 13),
|
||||
LastUsedBit = (1U << 14),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
@@ -133,16 +138,17 @@ struct MessageUpdate {
|
||||
enum class Flag : uint32 {
|
||||
None = 0,
|
||||
|
||||
Edited = (1U << 0),
|
||||
Destroyed = (1U << 1),
|
||||
DialogRowRepaint = (1U << 2),
|
||||
DialogRowRefresh = (1U << 3),
|
||||
NewAdded = (1U << 4),
|
||||
ReplyMarkup = (1U << 5),
|
||||
BotCallbackSent = (1U << 6),
|
||||
NewMaybeAdded = (1U << 7),
|
||||
Edited = (1U << 0),
|
||||
Destroyed = (1U << 1),
|
||||
DialogRowRepaint = (1U << 2),
|
||||
DialogRowRefresh = (1U << 3),
|
||||
NewAdded = (1U << 4),
|
||||
ReplyMarkup = (1U << 5),
|
||||
BotCallbackSent = (1U << 6),
|
||||
NewMaybeAdded = (1U << 7),
|
||||
RepliesUnreadCount = (1U << 8),
|
||||
|
||||
LastUsedBit = (1U << 7),
|
||||
LastUsedBit = (1U << 7),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
|
||||
@@ -887,6 +887,7 @@ void ApplyChannelUpdate(
|
||||
session->changes().peerUpdated(channel, UpdateFlag::StickersSet);
|
||||
}
|
||||
}
|
||||
channel->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
|
||||
channel->fullUpdated();
|
||||
|
||||
if (canViewAdmins != channel->canViewAdmins()
|
||||
|
||||
@@ -428,6 +428,7 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
|
||||
SetTopPinnedMessageId(chat, pinned->v);
|
||||
}
|
||||
chat->checkFolder(update.vfolder_id().value_or_empty());
|
||||
chat->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
|
||||
chat->fullUpdated();
|
||||
chat->setAbout(qs(update.vabout()));
|
||||
|
||||
|
||||
@@ -31,21 +31,83 @@ constexpr auto kReloadTimeout = 3600 * crl::time(1000);
|
||||
|
||||
CloudTheme CloudTheme::Parse(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDtheme &data) {
|
||||
const MTPDtheme &data,
|
||||
bool parseSettings) {
|
||||
const auto document = data.vdocument();
|
||||
const auto paper = [&]() -> std::optional<WallPaper> {
|
||||
if (const auto settings = data.vsettings()) {
|
||||
return settings->match([&](const MTPDthemeSettings &data) {
|
||||
return data.vwallpaper()
|
||||
? WallPaper::Create(session, *data.vwallpaper())
|
||||
: std::nullopt;
|
||||
});
|
||||
}
|
||||
return {};
|
||||
};
|
||||
const auto outgoingMessagesColors = [&] {
|
||||
auto result = std::vector<QColor>();
|
||||
if (const auto settings = data.vsettings()) {
|
||||
settings->match([&](const MTPDthemeSettings &data) {
|
||||
if (const auto colors = data.vmessage_colors()) {
|
||||
for (const auto color : colors->v) {
|
||||
result.push_back(ColorFromSerialized(color));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const auto accentColor = [&]() -> std::optional<QColor> {
|
||||
if (const auto settings = data.vsettings()) {
|
||||
return settings->match([&](const MTPDthemeSettings &data) {
|
||||
return ColorFromSerialized(data.vaccent_color().v);
|
||||
});
|
||||
}
|
||||
return {};
|
||||
};
|
||||
const auto basedOnDark = [&] {
|
||||
if (const auto settings = data.vsettings()) {
|
||||
return settings->match([&](const MTPDthemeSettings &data) {
|
||||
return data.vbase_theme().match([](
|
||||
const MTPDbaseThemeNight &) {
|
||||
return true;
|
||||
}, [](const MTPDbaseThemeTinted &) {
|
||||
return true;
|
||||
}, [](const auto &) {
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return {
|
||||
data.vid().v,
|
||||
data.vaccess_hash().v,
|
||||
qs(data.vslug()),
|
||||
qs(data.vtitle()),
|
||||
(document
|
||||
.id = data.vid().v,
|
||||
.accessHash = data.vaccess_hash().v,
|
||||
.slug = qs(data.vslug()),
|
||||
.title = qs(data.vtitle()),
|
||||
.documentId = (document
|
||||
? session->data().processDocument(*document)->id
|
||||
: DocumentId(0)),
|
||||
data.is_creator() ? session->userId() : UserId(0),
|
||||
data.vinstalls_count().v
|
||||
.createdBy = data.is_creator() ? session->userId() : UserId(0),
|
||||
.usersCount = data.vinstalls_count().value_or_empty(),
|
||||
.paper = parseSettings ? paper() : std::nullopt,
|
||||
.accentColor = parseSettings ? accentColor() : std::nullopt,
|
||||
.outgoingMessagesColors = (parseSettings
|
||||
? outgoingMessagesColors()
|
||||
: std::vector<QColor>()),
|
||||
.basedOnDark = parseSettings && basedOnDark(),
|
||||
};
|
||||
}
|
||||
|
||||
CloudTheme CloudTheme::Parse(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPTheme &data,
|
||||
bool parseSettings) {
|
||||
return data.match([&](const MTPDtheme &data) {
|
||||
return CloudTheme::Parse(session, data, parseSettings);
|
||||
});
|
||||
}
|
||||
|
||||
QString CloudThemes::Format() {
|
||||
static const auto kResult = QString::fromLatin1("tdesktop");
|
||||
return kResult;
|
||||
@@ -256,14 +318,14 @@ void CloudThemes::scheduleReload() {
|
||||
}
|
||||
|
||||
void CloudThemes::refresh() {
|
||||
if (_refreshRquestId) {
|
||||
if (_refreshRequestId) {
|
||||
return;
|
||||
}
|
||||
_refreshRquestId = _session->api().request(MTPaccount_GetThemes(
|
||||
_refreshRequestId = _session->api().request(MTPaccount_GetThemes(
|
||||
MTP_string(Format()),
|
||||
MTP_int(_hash)
|
||||
)).done([=](const MTPaccount_Themes &result) {
|
||||
_refreshRquestId = 0;
|
||||
_refreshRequestId = 0;
|
||||
result.match([&](const MTPDaccount_themes &data) {
|
||||
_hash = data.vhash().v;
|
||||
parseThemes(data.vthemes().v);
|
||||
@@ -271,7 +333,7 @@ void CloudThemes::refresh() {
|
||||
}, [](const MTPDaccount_themesNotModified &) {
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_refreshRquestId = 0;
|
||||
_refreshRequestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
@@ -279,13 +341,79 @@ void CloudThemes::parseThemes(const QVector<MTPTheme> &list) {
|
||||
_list.clear();
|
||||
_list.reserve(list.size());
|
||||
for (const auto &theme : list) {
|
||||
theme.match([&](const MTPDtheme &data) {
|
||||
_list.push_back(CloudTheme::Parse(_session, data));
|
||||
});
|
||||
_list.push_back(CloudTheme::Parse(_session, theme));
|
||||
}
|
||||
checkCurrentTheme();
|
||||
}
|
||||
|
||||
void CloudThemes::refreshChatThemes() {
|
||||
if (_chatThemesRequestId) {
|
||||
return;
|
||||
}
|
||||
_chatThemesRequestId = _session->api().request(MTPaccount_GetChatThemes(
|
||||
MTP_int(_chatThemesHash)
|
||||
)).done([=](const MTPaccount_ChatThemes &result) {
|
||||
_chatThemesRequestId = 0;
|
||||
result.match([&](const MTPDaccount_chatThemes &data) {
|
||||
_hash = data.vhash().v;
|
||||
parseChatThemes(data.vthemes().v);
|
||||
_chatThemesUpdates.fire({});
|
||||
}, [](const MTPDaccount_chatThemesNotModified &) {
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_chatThemesRequestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
const std::vector<ChatTheme> &CloudThemes::chatThemes() const {
|
||||
return _chatThemes;
|
||||
}
|
||||
|
||||
rpl::producer<> CloudThemes::chatThemesUpdated() const {
|
||||
return _chatThemesUpdates.events();
|
||||
}
|
||||
|
||||
std::optional<ChatTheme> CloudThemes::themeForEmoji(
|
||||
const QString &emoji) const {
|
||||
if (emoji.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
const auto i = ranges::find(_chatThemes, emoji, &ChatTheme::emoji);
|
||||
return (i != end(_chatThemes)) ? std::make_optional(*i) : std::nullopt;
|
||||
}
|
||||
|
||||
rpl::producer<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue(
|
||||
const QString &emoji) {
|
||||
if (emoji.isEmpty()) {
|
||||
return rpl::single<std::optional<ChatTheme>>(std::nullopt);
|
||||
} else if (auto result = themeForEmoji(emoji)) {
|
||||
return rpl::single(std::move(result));
|
||||
}
|
||||
refreshChatThemes();
|
||||
return rpl::single<std::optional<ChatTheme>>(
|
||||
std::nullopt
|
||||
) | rpl::then(chatThemesUpdated(
|
||||
) | rpl::map([=] {
|
||||
return themeForEmoji(emoji);
|
||||
}) | rpl::filter([](const std::optional<ChatTheme> &theme) {
|
||||
return theme.has_value();
|
||||
}) | rpl::take(1));
|
||||
}
|
||||
|
||||
void CloudThemes::parseChatThemes(const QVector<MTPChatTheme> &list) {
|
||||
_chatThemes.clear();
|
||||
_chatThemes.reserve(list.size());
|
||||
for (const auto &theme : list) {
|
||||
theme.match([&](const MTPDchatTheme &data) {
|
||||
_chatThemes.push_back({
|
||||
.emoji = qs(data.vemoticon()),
|
||||
.light = CloudTheme::Parse(_session, data.vtheme(), true),
|
||||
.dark = CloudTheme::Parse(_session, data.vdark_theme(), true),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void CloudThemes::checkCurrentTheme() {
|
||||
const auto &object = Window::Theme::Background()->themeObject();
|
||||
if (!object.cloud.id || !object.cloud.documentId) {
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "base/timer.h"
|
||||
#include "data/data_wall_paper.h"
|
||||
|
||||
class DocumentData;
|
||||
|
||||
@@ -32,9 +33,25 @@ struct CloudTheme {
|
||||
UserId createdBy = 0;
|
||||
int usersCount = 0;
|
||||
|
||||
std::optional<WallPaper> paper;
|
||||
std::optional<QColor> accentColor;
|
||||
std::vector<QColor> outgoingMessagesColors;
|
||||
bool basedOnDark = false;
|
||||
|
||||
static CloudTheme Parse(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDtheme &data);
|
||||
const MTPDtheme &data,
|
||||
bool parseSettings = false);
|
||||
static CloudTheme Parse(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPTheme &data,
|
||||
bool parseSettings = false);
|
||||
};
|
||||
|
||||
struct ChatTheme {
|
||||
QString emoji;
|
||||
CloudTheme light;
|
||||
CloudTheme dark;
|
||||
};
|
||||
|
||||
class CloudThemes final {
|
||||
@@ -49,6 +66,14 @@ public:
|
||||
void savedFromEditor(const CloudTheme &data);
|
||||
void remove(uint64 cloudThemeId);
|
||||
|
||||
void refreshChatThemes();
|
||||
[[nodiscard]] const std::vector<ChatTheme> &chatThemes() const;
|
||||
[[nodiscard]] rpl::producer<> chatThemesUpdated() const;
|
||||
[[nodiscard]] std::optional<ChatTheme> themeForEmoji(
|
||||
const QString &emoji) const;
|
||||
[[nodiscard]] rpl::producer<std::optional<ChatTheme>> themeForEmojiValue(
|
||||
const QString &emoji);
|
||||
|
||||
void applyUpdate(const MTPTheme &theme);
|
||||
|
||||
void resolve(
|
||||
@@ -90,13 +115,20 @@ private:
|
||||
Fn<void(std::shared_ptr<Data::DocumentMedia>)> callback);
|
||||
void invokeForLoaded(LoadingDocument &value);
|
||||
|
||||
void parseChatThemes(const QVector<MTPChatTheme> &list);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
int32 _hash = 0;
|
||||
mtpRequestId _refreshRquestId = 0;
|
||||
mtpRequestId _refreshRequestId = 0;
|
||||
mtpRequestId _resolveRequestId = 0;
|
||||
std::vector<CloudTheme> _list;
|
||||
rpl::event_stream<> _updates;
|
||||
|
||||
int32 _chatThemesHash = 0;
|
||||
mtpRequestId _chatThemesRequestId = 0;
|
||||
std::vector<ChatTheme> _chatThemes;
|
||||
rpl::event_stream<> _chatThemesUpdates;
|
||||
|
||||
base::Timer _reloadCurrentTimer;
|
||||
LoadingDocument _updatingFrom;
|
||||
LoadingDocument _previewFrom;
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_countries.h"
|
||||
|
||||
namespace Data {
|
||||
namespace {
|
||||
|
||||
const std::array<CountryInfo, 231> List = { {
|
||||
{ "Afghanistan", "AF", "93" },
|
||||
{ "Albania", "AL", "355" },
|
||||
{ "Algeria", "DZ", "213" },
|
||||
{ "American Samoa", "AS", "1684" },
|
||||
{ "Andorra", "AD", "376" },
|
||||
{ "Angola", "AO", "244" },
|
||||
{ "Anguilla", "AI", "1264" },
|
||||
{ "Antigua & Barbuda", "AG", "1268" },
|
||||
{ "Argentina", "AR", "54" },
|
||||
{ "Armenia", "AM", "374" },
|
||||
{ "Aruba", "AW", "297" },
|
||||
{ "Australia", "AU", "61" },
|
||||
{ "Austria", "AT", "43" },
|
||||
{ "Azerbaijan", "AZ", "994" },
|
||||
{ "Bahamas", "BS", "1242" },
|
||||
{ "Bahrain", "BH", "973" },
|
||||
{ "Bangladesh", "BD", "880" },
|
||||
{ "Barbados", "BB", "1246" },
|
||||
{ "Belarus", "BY", "375" },
|
||||
{ "Belgium", "BE", "32" },
|
||||
{ "Belize", "BZ", "501" },
|
||||
{ "Benin", "BJ", "229" },
|
||||
{ "Bermuda", "BM", "1441" },
|
||||
{ "Bhutan", "BT", "975" },
|
||||
{ "Bolivia", "BO", "591" },
|
||||
{ "Bonaire, Sint Eustatius & Saba", "BQ", "599" },
|
||||
{ "Bosnia & Herzegovina", "BA", "387" },
|
||||
{ "Botswana", "BW", "267" },
|
||||
{ "Brazil", "BR", "55" },
|
||||
{ "British Virgin Islands", "VG", "1284" },
|
||||
{ "Brunei Darussalam", "BN", "673" },
|
||||
{ "Bulgaria", "BG", "359" },
|
||||
{ "Burkina Faso", "BF", "226" },
|
||||
{ "Burundi", "BI", "257" },
|
||||
{ "Cambodia", "KH", "855" },
|
||||
{ "Cameroon", "CM", "237" },
|
||||
{ "Canada", "CA", "1" },
|
||||
{ "Cape Verde", "CV", "238" },
|
||||
{ "Cayman Islands", "KY", "1345" },
|
||||
{ "Central African Republic", "CF", "236" },
|
||||
{ "Chad", "TD", "235" },
|
||||
{ "Chile", "CL", "56" },
|
||||
{ "China", "CN", "86" },
|
||||
{ "Colombia", "CO", "57" },
|
||||
{ "Comoros", "KM", "269" },
|
||||
{ "Congo", "CG", "242" },
|
||||
{ "Congo, Democratic Republic", "CD", "243" },
|
||||
{ "Cook Islands", "CK", "682" },
|
||||
{ "Costa Rica", "CR", "506" },
|
||||
{ "Croatia", "HR", "385" },
|
||||
{ "Cuba", "CU", "53" },
|
||||
{ "Curaçao", "CW", "599" },
|
||||
{ "Cyprus", "CY", "357" },
|
||||
{ "Czech Republic", "CZ", "420" },
|
||||
{ "Côte d`Ivoire", "CI", "225" },
|
||||
{ "Denmark", "DK", "45" },
|
||||
{ "Diego Garcia", "IO", "246" },
|
||||
{ "Djibouti", "DJ", "253" },
|
||||
{ "Dominica", "DM", "1767" },
|
||||
{ "Dominican Republic", "DO", "1" },
|
||||
{ "Ecuador", "EC", "593" },
|
||||
{ "Egypt", "EG", "20" },
|
||||
{ "El Salvador", "SV", "503" },
|
||||
{ "Equatorial Guinea", "GQ", "240" },
|
||||
{ "Eritrea", "ER", "291" },
|
||||
{ "Estonia", "EE", "372" },
|
||||
{ "Ethiopia", "ET", "251" },
|
||||
{ "Falkland Islands", "FK", "500" },
|
||||
{ "Faroe Islands", "FO", "298" },
|
||||
{ "Fiji", "FJ", "679" },
|
||||
{ "Finland", "FI", "358" },
|
||||
{ "France", "FR", "33" },
|
||||
{ "French Guiana", "GF", "594" },
|
||||
{ "French Polynesia", "PF", "689" },
|
||||
{ "Gabon", "GA", "241" },
|
||||
{ "Gambia", "GM", "220" },
|
||||
{ "Georgia", "GE", "995" },
|
||||
{ "Germany", "DE", "49" },
|
||||
{ "Ghana", "GH", "233" },
|
||||
{ "Gibraltar", "GI", "350" },
|
||||
{ "Greece", "GR", "30" },
|
||||
{ "Greenland", "GL", "299" },
|
||||
{ "Grenada", "GD", "1473" },
|
||||
{ "Guadeloupe", "GP", "590" },
|
||||
{ "Guam", "GU", "1671" },
|
||||
{ "Guatemala", "GT", "502" },
|
||||
{ "Guinea", "GN", "224" },
|
||||
{ "Guinea-Bissau", "GW", "245" },
|
||||
{ "Guyana", "GY", "592" },
|
||||
{ "Haiti", "HT", "509" },
|
||||
{ "Honduras", "HN", "504" },
|
||||
{ "Hong Kong", "HK", "852" },
|
||||
{ "Hungary", "HU", "36" },
|
||||
{ "Iceland", "IS", "354" },
|
||||
{ "India", "IN", "91" },
|
||||
{ "Indonesia", "ID", "62" },
|
||||
{ "Iran", "IR", "98" },
|
||||
{ "Iraq", "IQ", "964" },
|
||||
{ "Ireland", "IE", "353" },
|
||||
{ "Israel", "IL", "972" },
|
||||
{ "Italy", "IT", "39" },
|
||||
{ "Jamaica", "JM", "1876" },
|
||||
{ "Japan", "JP", "81" },
|
||||
{ "Jordan", "JO", "962" },
|
||||
{ "Kazakhstan", "KZ", "7" },
|
||||
{ "Kenya", "KE", "254" },
|
||||
{ "Kiribati", "KI", "686" },
|
||||
{ "Kuwait", "KW", "965" },
|
||||
{ "Kyrgyzstan", "KG", "996" },
|
||||
{ "Laos", "LA", "856" },
|
||||
{ "Latvia", "LV", "371" },
|
||||
{ "Lebanon", "LB", "961" },
|
||||
{ "Lesotho", "LS", "266" },
|
||||
{ "Liberia", "LR", "231" },
|
||||
{ "Libya", "LY", "218" },
|
||||
{ "Liechtenstein", "LI", "423" },
|
||||
{ "Lithuania", "LT", "370" },
|
||||
{ "Luxembourg", "LU", "352" },
|
||||
{ "Macau", "MO", "853" },
|
||||
{ "Macedonia", "MK", "389" },
|
||||
{ "Madagascar", "MG", "261" },
|
||||
{ "Malawi", "MW", "265" },
|
||||
{ "Malaysia", "MY", "60" },
|
||||
{ "Maldives", "MV", "960" },
|
||||
{ "Mali", "ML", "223" },
|
||||
{ "Malta", "MT", "356" },
|
||||
{ "Marshall Islands", "MH", "692" },
|
||||
{ "Martinique", "MQ", "596" },
|
||||
{ "Mauritania", "MR", "222" },
|
||||
{ "Mauritius", "MU", "230" },
|
||||
{ "Mexico", "MX", "52" },
|
||||
{ "Micronesia", "FM", "691" },
|
||||
{ "Moldova", "MD", "373" },
|
||||
{ "Monaco", "MC", "377" },
|
||||
{ "Mongolia", "MN", "976" },
|
||||
{ "Montenegro", "ME", "382" },
|
||||
{ "Montserrat", "MS", "1664" },
|
||||
{ "Morocco", "MA", "212" },
|
||||
{ "Mozambique", "MZ", "258" },
|
||||
{ "Myanmar", "MM", "95" },
|
||||
{ "Namibia", "NA", "264" },
|
||||
{ "Nauru", "NR", "674" },
|
||||
{ "Nepal", "NP", "977" },
|
||||
{ "Netherlands", "NL", "31" },
|
||||
{ "New Caledonia", "NC", "687" },
|
||||
{ "New Zealand", "NZ", "64" },
|
||||
{ "Nicaragua", "NI", "505" },
|
||||
{ "Niger", "NE", "227" },
|
||||
{ "Nigeria", "NG", "234" },
|
||||
{ "Niue", "NU", "683" },
|
||||
{ "Norfolk Island", "NF", "672" },
|
||||
{ "North Korea", "KP", "850" },
|
||||
{ "Northern Mariana Islands", "MP", "1670" },
|
||||
{ "Norway", "NO", "47" },
|
||||
{ "Oman", "OM", "968" },
|
||||
{ "Pakistan", "PK", "92" },
|
||||
{ "Palau", "PW", "680" },
|
||||
{ "Palestine", "PS", "970" },
|
||||
{ "Panama", "PA", "507" },
|
||||
{ "Papua New Guinea", "PG", "675" },
|
||||
{ "Paraguay", "PY", "595" },
|
||||
{ "Peru", "PE", "51" },
|
||||
{ "Philippines", "PH", "63" },
|
||||
{ "Poland", "PL", "48" },
|
||||
{ "Portugal", "PT", "351" },
|
||||
{ "Puerto Rico", "PR", "1" },
|
||||
{ "Qatar", "QA", "974" },
|
||||
{ "Romania", "RO", "40" },
|
||||
{ "Russian Federation", "RU", "7" },
|
||||
{ "Rwanda", "RW", "250" },
|
||||
{ "Réunion", "RE", "262" },
|
||||
{ "Saint Helena", "SH", "247" },
|
||||
{ "Saint Helena", "SH2", "290" },
|
||||
{ "Saint Kitts & Nevis", "KN", "1869" },
|
||||
{ "Saint Lucia", "LC", "1758" },
|
||||
{ "Saint Pierre & Miquelon", "PM", "508" },
|
||||
{ "Saint Vincent & the Grenadines", "VC", "1784" },
|
||||
{ "Samoa", "WS", "685" },
|
||||
{ "San Marino", "SM", "378" },
|
||||
{ "Saudi Arabia", "SA", "966" },
|
||||
{ "Senegal", "SN", "221" },
|
||||
{ "Serbia", "RS", "381" },
|
||||
{ "Seychelles", "SC", "248" },
|
||||
{ "Sierra Leone", "SL", "232" },
|
||||
{ "Singapore", "SG", "65" },
|
||||
{ "Sint Maarten", "SX", "1721" },
|
||||
{ "Slovakia", "SK", "421" },
|
||||
{ "Slovenia", "SI", "386" },
|
||||
{ "Solomon Islands", "SB", "677" },
|
||||
{ "Somalia", "SO", "252" },
|
||||
{ "South Africa", "ZA", "27" },
|
||||
{ "South Korea", "KR", "82" },
|
||||
{ "South Sudan", "SS", "211" },
|
||||
{ "Spain", "ES", "34" },
|
||||
{ "Sri Lanka", "LK", "94" },
|
||||
{ "Sudan", "SD", "249" },
|
||||
{ "Suriname", "SR", "597" },
|
||||
{ "Swaziland", "SZ", "268" },
|
||||
{ "Sweden", "SE", "46" },
|
||||
{ "Switzerland", "CH", "41" },
|
||||
{ "Syrian Arab Republic", "SY", "963" },
|
||||
{ "São Tomé & Príncipe", "ST", "239" },
|
||||
{ "Taiwan", "TW", "886" },
|
||||
{ "Tajikistan", "TJ", "992" },
|
||||
{ "Tanzania", "TZ", "255" },
|
||||
{ "Thailand", "TH", "66" },
|
||||
{ "Timor-Leste", "TL", "670" },
|
||||
{ "Togo", "TG", "228" },
|
||||
{ "Tokelau", "TK", "690" },
|
||||
{ "Tonga", "TO", "676" },
|
||||
{ "Trinidad & Tobago", "TT", "1868" },
|
||||
{ "Tunisia", "TN", "216" },
|
||||
{ "Turkey", "TR", "90" },
|
||||
{ "Turkmenistan", "TM", "993" },
|
||||
{ "Turks & Caicos Islands", "TC", "1649" },
|
||||
{ "Tuvalu", "TV", "688" },
|
||||
{ "US Virgin Islands", "VI", "1340" },
|
||||
{ "USA", "US", "1", "United States of America" },
|
||||
{ "Uganda", "UG", "256" },
|
||||
{ "Ukraine", "UA", "380" },
|
||||
{ "United Arab Emirates", "AE", "971" },
|
||||
{ "United Kingdom", "GB", "44" },
|
||||
{ "Uruguay", "UY", "598" },
|
||||
{ "Uzbekistan", "UZ", "998" },
|
||||
{ "Vanuatu", "VU", "678" },
|
||||
{ "Venezuela", "VE", "58" },
|
||||
{ "Vietnam", "VN", "84" },
|
||||
{ "Wallis & Futuna", "WF", "681" },
|
||||
{ "Yemen", "YE", "967" },
|
||||
{ "Zambia", "ZM", "260" },
|
||||
{ "Zimbabwe", "ZW", "263" },
|
||||
} };
|
||||
|
||||
QHash<QString, const CountryInfo *> ByCode;
|
||||
QHash<QString, const CountryInfo *> ByISO2;
|
||||
|
||||
} // namespace
|
||||
|
||||
const std::array<CountryInfo, 231> &Countries() {
|
||||
return List;
|
||||
}
|
||||
|
||||
const QHash<QString, const CountryInfo *> &CountriesByCode() {
|
||||
if (ByCode.isEmpty()) {
|
||||
ByCode.reserve(List.size());
|
||||
for (const auto &entry : List) {
|
||||
ByCode.insert(entry.code, &entry);
|
||||
}
|
||||
}
|
||||
return ByCode;
|
||||
}
|
||||
|
||||
const QHash<QString, const CountryInfo *> &CountriesByISO2() {
|
||||
if (ByISO2.isEmpty()) {
|
||||
ByISO2.reserve(List.size());
|
||||
for (const auto &entry : List) {
|
||||
ByISO2.insert(entry.iso2, &entry);
|
||||
}
|
||||
}
|
||||
return ByISO2;
|
||||
}
|
||||
|
||||
QString ValidPhoneCode(QString fullCode) {
|
||||
const auto &byCode = CountriesByCode();
|
||||
while (fullCode.length()) {
|
||||
const auto i = byCode.constFind(fullCode);
|
||||
if (i != byCode.cend()) {
|
||||
return (*i)->code;
|
||||
}
|
||||
fullCode.chop(1);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString CountryNameByISO2(const QString &iso) {
|
||||
const auto &byISO2 = CountriesByISO2();
|
||||
const auto i = byISO2.constFind(iso);
|
||||
return (i != byISO2.cend()) ? QString::fromUtf8((*i)->name) : QString();
|
||||
}
|
||||
|
||||
QString CountryISO2ByPhone(const QString &phone) {
|
||||
const auto &byCode = Data::CountriesByCode();
|
||||
const auto code = Data::ValidPhoneCode(phone);
|
||||
const auto i = byCode.find(code);
|
||||
return (i != byCode.cend()) ? QString::fromUtf8((*i)->iso2) : QString();
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Data {
|
||||
|
||||
struct CountryInfo {
|
||||
const char *name = nullptr;
|
||||
const char *iso2 = nullptr;
|
||||
const char *code = nullptr;
|
||||
const char *alternativeName = nullptr;
|
||||
};
|
||||
|
||||
[[nodiscard]] const std::array<CountryInfo, 231> &Countries();
|
||||
|
||||
[[nodiscard]] const QHash<QString, const CountryInfo *> &CountriesByCode();
|
||||
[[nodiscard]] const QHash<QString, const CountryInfo *> &CountriesByISO2();
|
||||
|
||||
[[nodiscard]] QString ValidPhoneCode(QString fullCode);
|
||||
[[nodiscard]] QString CountryNameByISO2(const QString &iso);
|
||||
[[nodiscard]] QString CountryISO2ByPhone(const QString &phone);
|
||||
|
||||
} // namespace Data
|
||||
@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "platform/platform_file_utilities.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "window/window_session_controller.h"
|
||||
|
||||
@@ -33,8 +34,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Data {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxWallpaperSize = 3072;
|
||||
|
||||
void LaunchWithWarning(
|
||||
// not_null<Window::Controller*> controller,
|
||||
const QString &name,
|
||||
@@ -175,19 +174,7 @@ bool IsIpRevealingName(const QString &filepath) {
|
||||
);
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage ReadImage(
|
||||
const QString &path,
|
||||
const QByteArray &content,
|
||||
bool gzipSvg) {
|
||||
return Images::Read({
|
||||
.path = path,
|
||||
.content = content,
|
||||
.maxSize = QSize(kMaxWallpaperSize, kMaxWallpaperSize),
|
||||
.gzipSvg = gzipSvg,
|
||||
}).image;
|
||||
}
|
||||
|
||||
base::binary_guard ReadImageAsync(
|
||||
base::binary_guard ReadBackgroundImageAsync(
|
||||
not_null<Data::DocumentMedia*> media,
|
||||
FnMut<QImage(QImage)> postprocess,
|
||||
FnMut<void(QImage&&)> done) {
|
||||
@@ -201,7 +188,7 @@ base::binary_guard ReadImageAsync(
|
||||
guard = result.make_guard(),
|
||||
callback = std::move(done)
|
||||
]() mutable {
|
||||
auto image = ReadImage(path, bytes, gzipSvg);
|
||||
auto image = Ui::ReadBackgroundImage(path, bytes, gzipSvg);
|
||||
if (postprocess) {
|
||||
image = postprocess(std::move(image));
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class DocumentMedia;
|
||||
// [[nodiscard]] bool IsValidMediaFile(const QString &filepath);
|
||||
[[nodiscard]] bool IsExecutableName(const QString &filepath);
|
||||
[[nodiscard]] bool IsIpRevealingName(const QString &filepath);
|
||||
base::binary_guard ReadImageAsync(
|
||||
base::binary_guard ReadBackgroundImageAsync(
|
||||
not_null<Data::DocumentMedia*> media,
|
||||
FnMut<QImage(QImage)> postprocess,
|
||||
FnMut<void(QImage&&)> done);
|
||||
|
||||
@@ -496,8 +496,9 @@ void GroupCall::reload() {
|
||||
}
|
||||
_reloadByQueuedUpdatesTimer.cancel();
|
||||
|
||||
const auto limit = 3;
|
||||
_reloadRequestId = api().request(
|
||||
MTPphone_GetGroupCall(input())
|
||||
MTPphone_GetGroupCall(input(), MTP_int(limit))
|
||||
).done([=](const MTPphone_GroupCall &result) {
|
||||
if (requestParticipantsAfterReload(result)) {
|
||||
_savedFull = result;
|
||||
|
||||
@@ -201,7 +201,7 @@ bool Media::canBeGrouped() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
QString Media::chatListText() const {
|
||||
QString Media::chatListText(DrawInDialog way) const {
|
||||
auto result = notificationText();
|
||||
return result.isEmpty()
|
||||
? QString()
|
||||
@@ -341,10 +341,11 @@ QString MediaPhoto::notificationText() const {
|
||||
parent()->originalText().text);
|
||||
}
|
||||
|
||||
QString MediaPhoto::chatListText() const {
|
||||
return WithCaptionDialogsText(
|
||||
tr::lng_in_dlg_photo(tr::now),
|
||||
parent()->originalText().text);
|
||||
QString MediaPhoto::chatListText(DrawInDialog way) const {
|
||||
const auto caption = (way == DrawInDialog::WithoutSenderAndCaption)
|
||||
? QString()
|
||||
: parent()->originalText().text;
|
||||
return WithCaptionDialogsText(tr::lng_in_dlg_photo(tr::now), caption);
|
||||
}
|
||||
|
||||
QString MediaPhoto::pinnedTextSubstring() const {
|
||||
@@ -510,9 +511,9 @@ bool MediaFile::replyPreviewLoaded() const {
|
||||
return _document->replyPreviewLoaded();
|
||||
}
|
||||
|
||||
QString MediaFile::chatListText() const {
|
||||
QString MediaFile::chatListText(DrawInDialog way) const {
|
||||
if (const auto sticker = _document->sticker()) {
|
||||
return Media::chatListText();
|
||||
return Media::chatListText(way);
|
||||
}
|
||||
const auto type = [&] {
|
||||
using namespace Ui::Text;
|
||||
@@ -532,7 +533,10 @@ QString MediaFile::chatListText() const {
|
||||
}
|
||||
return tr::lng_in_dlg_file(tr::now);
|
||||
}();
|
||||
return WithCaptionDialogsText(type, parent()->originalText().text);
|
||||
const auto caption = (way == DrawInDialog::WithoutSenderAndCaption)
|
||||
? QString()
|
||||
: parent()->originalText().text;
|
||||
return WithCaptionDialogsText(type, caption);
|
||||
}
|
||||
|
||||
QString MediaFile::notificationText() const {
|
||||
@@ -851,7 +855,7 @@ Data::CloudImage *MediaLocation::location() const {
|
||||
return _location;
|
||||
}
|
||||
|
||||
QString MediaLocation::chatListText() const {
|
||||
QString MediaLocation::chatListText(DrawInDialog way) const {
|
||||
return WithCaptionDialogsText(tr::lng_maps_point(tr::now), _title);
|
||||
}
|
||||
|
||||
@@ -1045,7 +1049,7 @@ bool MediaWebPage::replyPreviewLoaded() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
QString MediaWebPage::chatListText() const {
|
||||
QString MediaWebPage::chatListText(DrawInDialog way) const {
|
||||
return notificationText();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace HistoryView {
|
||||
enum class Context : char;
|
||||
class Element;
|
||||
class Media;
|
||||
enum class DrawInDialog;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace Data {
|
||||
@@ -72,6 +73,8 @@ public:
|
||||
|
||||
not_null<HistoryItem*> parent() const;
|
||||
|
||||
using DrawInDialog = HistoryView::DrawInDialog;
|
||||
|
||||
virtual std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) = 0;
|
||||
|
||||
virtual DocumentData *document() const;
|
||||
@@ -92,7 +95,7 @@ public:
|
||||
virtual bool replyPreviewLoaded() const;
|
||||
// Returns text with link-start and link-end commands for service-color highlighting.
|
||||
// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
|
||||
virtual QString chatListText() const;
|
||||
virtual QString chatListText(DrawInDialog way) const;
|
||||
virtual QString notificationText() const = 0;
|
||||
virtual QString pinnedTextSubstring() const = 0;
|
||||
virtual TextForMimeData clipboardText() const = 0;
|
||||
@@ -148,7 +151,7 @@ public:
|
||||
bool hasReplyPreview() const override;
|
||||
Image *replyPreview() const override;
|
||||
bool replyPreviewLoaded() const override;
|
||||
QString chatListText() const override;
|
||||
QString chatListText(DrawInDialog way) const override;
|
||||
QString notificationText() const override;
|
||||
QString pinnedTextSubstring() const override;
|
||||
TextForMimeData clipboardText() const override;
|
||||
@@ -186,7 +189,7 @@ public:
|
||||
bool hasReplyPreview() const override;
|
||||
Image *replyPreview() const override;
|
||||
bool replyPreviewLoaded() const override;
|
||||
QString chatListText() const override;
|
||||
QString chatListText(DrawInDialog way) const override;
|
||||
QString notificationText() const override;
|
||||
QString pinnedTextSubstring() const override;
|
||||
TextForMimeData clipboardText() const override;
|
||||
@@ -252,7 +255,7 @@ public:
|
||||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||
|
||||
Data::CloudImage *location() const override;
|
||||
QString chatListText() const override;
|
||||
QString chatListText(DrawInDialog way) const override;
|
||||
QString notificationText() const override;
|
||||
QString pinnedTextSubstring() const override;
|
||||
TextForMimeData clipboardText() const override;
|
||||
@@ -320,7 +323,7 @@ public:
|
||||
bool hasReplyPreview() const override;
|
||||
Image *replyPreview() const override;
|
||||
bool replyPreviewLoaded() const override;
|
||||
QString chatListText() const override;
|
||||
QString chatListText(DrawInDialog way) const override;
|
||||
QString notificationText() const override;
|
||||
QString pinnedTextSubstring() const override;
|
||||
TextForMimeData clipboardText() const override;
|
||||
|
||||
@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "base/crc32hash.h"
|
||||
#include "lang/lang_keys.h"
|
||||
@@ -527,15 +528,6 @@ bool PeerData::canEditMessagesIndefinitely() const {
|
||||
Unexpected("Peer type in PeerData::canEditMessagesIndefinitely.");
|
||||
}
|
||||
|
||||
bool PeerData::hasPinnedMessages() const {
|
||||
return _hasPinnedMessages;
|
||||
}
|
||||
|
||||
void PeerData::setHasPinnedMessages(bool has) {
|
||||
_hasPinnedMessages = has;
|
||||
session().changes().peerUpdated(this, UpdateFlag::PinnedMessages);
|
||||
}
|
||||
|
||||
bool PeerData::canExportChatHistory() const {
|
||||
if (isRepliesChat()) {
|
||||
return false;
|
||||
@@ -1012,6 +1004,21 @@ PeerId PeerData::groupCallDefaultJoinAs() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PeerData::setThemeEmoji(const QString &emoji) {
|
||||
if (true || _themeEmoji == emoji) {
|
||||
return;
|
||||
}
|
||||
_themeEmoji = emoji;
|
||||
if (!emoji.isEmpty() && !owner().cloudThemes().themeForEmoji(emoji)) {
|
||||
owner().cloudThemes().refreshChatThemes();
|
||||
}
|
||||
session().changes().peerUpdated(this, UpdateFlag::ChatThemeEmoji);
|
||||
}
|
||||
|
||||
const QString &PeerData::themeEmoji() const {
|
||||
return _themeEmoji;
|
||||
}
|
||||
|
||||
void PeerData::setIsBlocked(bool is) {
|
||||
const auto status = is
|
||||
? BlockStatus::Blocked
|
||||
@@ -1154,7 +1161,7 @@ void SetTopPinnedMessageId(not_null<PeerData*> peer, MsgId messageId) {
|
||||
Storage::SharedMediaType::Pinned,
|
||||
messageId,
|
||||
{ messageId, ServerMaxMsgId }));
|
||||
peer->setHasPinnedMessages(true);
|
||||
peer->owner().history(peer)->setHasPinnedMessages(true);
|
||||
}
|
||||
|
||||
FullMsgId ResolveTopPinnedId(
|
||||
|
||||
@@ -406,9 +406,6 @@ public:
|
||||
[[nodiscard]] bool canPinMessages() const;
|
||||
[[nodiscard]] bool canEditMessagesIndefinitely() const;
|
||||
|
||||
[[nodiscard]] bool hasPinnedMessages() const;
|
||||
void setHasPinnedMessages(bool has);
|
||||
|
||||
[[nodiscard]] bool canExportChatHistory() const;
|
||||
|
||||
// Returns true if about text was changed.
|
||||
@@ -470,6 +467,9 @@ public:
|
||||
[[nodiscard]] Data::GroupCall *groupCall() const;
|
||||
[[nodiscard]] PeerId groupCallDefaultJoinAs() const;
|
||||
|
||||
void setThemeEmoji(const QString &emoji);
|
||||
[[nodiscard]] const QString &themeEmoji() const;
|
||||
|
||||
const PeerId id;
|
||||
QString name;
|
||||
MTPinputPeer input = MTP_inputPeerEmpty();
|
||||
@@ -508,13 +508,13 @@ private:
|
||||
crl::time _lastFullUpdate = 0;
|
||||
|
||||
TimeId _ttlPeriod = 0;
|
||||
bool _hasPinnedMessages = false;
|
||||
|
||||
Settings _settings = PeerSettings(PeerSetting::Unknown);
|
||||
BlockStatus _blockStatus = BlockStatus::Unknown;
|
||||
LoadedStatus _loadedStatus = LoadedStatus::Not;
|
||||
|
||||
QString _about;
|
||||
QString _themeEmoji;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -101,6 +101,15 @@ rpl::producer<MessagesSlice> RepliesList::source(
|
||||
_partLoaded.events(
|
||||
) | rpl::start_with_next(pushDelayed, lifetime);
|
||||
|
||||
_history->session().data().channelDifferenceTooLong(
|
||||
) | rpl::filter([=](not_null<ChannelData*> channel) {
|
||||
if (_history->peer != channel || !_skippedAfter.has_value()) {
|
||||
return false;
|
||||
}
|
||||
_skippedAfter = std::nullopt;
|
||||
return true;
|
||||
}) | rpl::start_with_next(pushDelayed, lifetime);
|
||||
|
||||
push();
|
||||
return lifetime;
|
||||
};
|
||||
@@ -169,6 +178,64 @@ rpl::producer<int> RepliesList::fullCount() const {
|
||||
return _fullCount.value() | rpl::filter_optional();
|
||||
}
|
||||
|
||||
std::optional<int> RepliesList::fullUnreadCountAfter(
|
||||
MsgId readTillId,
|
||||
MsgId wasReadTillId,
|
||||
std::optional<int> wasUnreadCountAfter) const {
|
||||
Expects(readTillId >= wasReadTillId);
|
||||
|
||||
readTillId = std::max(readTillId, _rootId);
|
||||
wasReadTillId = std::max(wasReadTillId, _rootId);
|
||||
const auto backLoaded = (_skippedBefore == 0);
|
||||
const auto frontLoaded = (_skippedAfter == 0);
|
||||
const auto fullLoaded = backLoaded && frontLoaded;
|
||||
const auto allUnread = (readTillId == _rootId)
|
||||
|| (fullLoaded && _list.empty());
|
||||
const auto countIncoming = [&](auto from, auto till) {
|
||||
auto &owner = _history->owner();
|
||||
const auto channelId = _history->channelId();
|
||||
auto count = 0;
|
||||
for (auto i = from; i != till; ++i) {
|
||||
if (!owner.message(channelId, *i)->out()) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
};
|
||||
if (allUnread && fullLoaded) {
|
||||
// Should not happen too often unless the list is empty.
|
||||
return countIncoming(begin(_list), end(_list));
|
||||
} else if (frontLoaded && !_list.empty() && readTillId >= _list.front()) {
|
||||
// Always "count by local data" if read till the end.
|
||||
return 0;
|
||||
} else if (wasReadTillId == readTillId) {
|
||||
// Otherwise don't recount the same value over and over.
|
||||
return wasUnreadCountAfter;
|
||||
} else if (frontLoaded && !_list.empty() && readTillId >= _list.back()) {
|
||||
// And count by local data if it is available and read-till changed.
|
||||
return countIncoming(
|
||||
begin(_list),
|
||||
ranges::lower_bound(_list, readTillId, std::greater<>()));
|
||||
} else if (_list.empty()) {
|
||||
return std::nullopt;
|
||||
} else if (wasUnreadCountAfter.has_value()
|
||||
&& (frontLoaded || readTillId <= _list.front())
|
||||
&& (backLoaded || wasReadTillId >= _list.back())) {
|
||||
// Count how many were read since previous value.
|
||||
const auto from = ranges::lower_bound(
|
||||
_list,
|
||||
readTillId,
|
||||
std::greater<>());
|
||||
const auto till = ranges::lower_bound(
|
||||
from,
|
||||
end(_list),
|
||||
wasReadTillId,
|
||||
std::greater<>());
|
||||
return std::max(*wasUnreadCountAfter - countIncoming(from, till), 0);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void RepliesList::injectRootMessageAndReverse(not_null<Viewer*> viewer) {
|
||||
injectRootMessage(viewer);
|
||||
ranges::reverse(viewer->slice.ids);
|
||||
|
||||
@@ -31,6 +31,11 @@ public:
|
||||
|
||||
[[nodiscard]] rpl::producer<int> fullCount() const;
|
||||
|
||||
[[nodiscard]] std::optional<int> fullUnreadCountAfter(
|
||||
MsgId readTillId,
|
||||
MsgId wasReadTillId,
|
||||
std::optional<int> wasUnreadCountAfter) const;
|
||||
|
||||
private:
|
||||
struct Viewer;
|
||||
|
||||
|
||||
153
Telegram/SourceFiles/data/data_send_action.cpp
Normal file
153
Telegram/SourceFiles/data/data_send_action.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_send_action.h"
|
||||
|
||||
#include "data/data_user.h"
|
||||
#include "history/history.h"
|
||||
#include "history/view/history_view_send_action.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
SendActionManager::SendActionManager()
|
||||
: _animation([=](crl::time now) { return callback(now); }) {
|
||||
}
|
||||
|
||||
HistoryView::SendActionPainter *SendActionManager::lookupPainter(
|
||||
not_null<History*> history,
|
||||
MsgId rootId) {
|
||||
if (!rootId) {
|
||||
return history->sendActionPainter();
|
||||
}
|
||||
const auto i = _painters.find(history);
|
||||
if (i == end(_painters)) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto j = i->second.find(rootId);
|
||||
if (j == end(i->second)) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto result = j->second.lock();
|
||||
if (!result) {
|
||||
i->second.erase(j);
|
||||
if (i->second.empty()) {
|
||||
_painters.erase(i);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
crl::on_main([copy = result] {
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
|
||||
void SendActionManager::registerFor(
|
||||
not_null<History*> history,
|
||||
MsgId rootId,
|
||||
not_null<UserData*> user,
|
||||
const MTPSendMessageAction &action,
|
||||
TimeId when) {
|
||||
if (history->peer->isSelf()) {
|
||||
return;
|
||||
}
|
||||
const auto sendAction = lookupPainter(history, rootId);
|
||||
if (!sendAction) {
|
||||
return;
|
||||
}
|
||||
if (sendAction->updateNeedsAnimating(user, action)) {
|
||||
user->madeAction(when);
|
||||
|
||||
if (!_sendActions.contains(std::pair{ history, rootId })) {
|
||||
_sendActions.emplace(std::pair{ history, rootId }, crl::now());
|
||||
_animation.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto SendActionManager::repliesPainter(
|
||||
not_null<History*> history,
|
||||
MsgId rootId)
|
||||
-> std::shared_ptr<SendActionPainter> {
|
||||
auto &weak = _painters[history][rootId];
|
||||
if (auto strong = weak.lock()) {
|
||||
return strong;
|
||||
}
|
||||
auto result = std::make_shared<SendActionPainter>(history);
|
||||
weak = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
void SendActionManager::repliesPainterRemoved(
|
||||
not_null<History*> history,
|
||||
MsgId rootId) {
|
||||
const auto i = _painters.find(history);
|
||||
if (i == end(_painters)) {
|
||||
return;
|
||||
}
|
||||
const auto j = i->second.find(rootId);
|
||||
if (j == end(i->second) || j->second.lock()) {
|
||||
return;
|
||||
}
|
||||
i->second.erase(j);
|
||||
if (i->second.empty()) {
|
||||
_painters.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
void SendActionManager::repliesPaintersClear(
|
||||
not_null<History*> history,
|
||||
not_null<UserData*> user) {
|
||||
auto &map = _painters[history];
|
||||
for (auto i = map.begin(); i != map.end();) {
|
||||
if (auto strong = i->second.lock()) {
|
||||
strong->clear(user);
|
||||
++i;
|
||||
} else {
|
||||
i = map.erase(i);
|
||||
}
|
||||
}
|
||||
if (map.empty()) {
|
||||
_painters.erase(history);
|
||||
}
|
||||
}
|
||||
|
||||
bool SendActionManager::callback(crl::time now) {
|
||||
for (auto i = begin(_sendActions); i != end(_sendActions);) {
|
||||
const auto sendAction = lookupPainter(
|
||||
i->first.first,
|
||||
i->first.second);
|
||||
if (sendAction && sendAction->updateNeedsAnimating(now)) {
|
||||
++i;
|
||||
} else {
|
||||
i = _sendActions.erase(i);
|
||||
}
|
||||
}
|
||||
return !_sendActions.empty();
|
||||
}
|
||||
|
||||
auto SendActionManager::animationUpdated() const
|
||||
-> rpl::producer<SendActionManager::AnimationUpdate> {
|
||||
return _animationUpdate.events();
|
||||
}
|
||||
|
||||
void SendActionManager::updateAnimation(AnimationUpdate &&update) {
|
||||
_animationUpdate.fire(std::move(update));
|
||||
}
|
||||
|
||||
auto SendActionManager::speakingAnimationUpdated() const
|
||||
-> rpl::producer<not_null<History*>> {
|
||||
return _speakingAnimationUpdate.events();
|
||||
}
|
||||
|
||||
void SendActionManager::updateSpeakingAnimation(not_null<History*> history) {
|
||||
_speakingAnimationUpdate.fire_copy(history);
|
||||
}
|
||||
|
||||
void SendActionManager::clear() {
|
||||
_sendActions.clear();
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
81
Telegram/SourceFiles/data/data_send_action.h
Normal file
81
Telegram/SourceFiles/data/data_send_action.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/effects/animations.h"
|
||||
|
||||
class History;
|
||||
|
||||
namespace HistoryView {
|
||||
class SendActionPainter;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace Data {
|
||||
|
||||
class SendActionManager final {
|
||||
public:
|
||||
struct AnimationUpdate {
|
||||
not_null<History*> history;
|
||||
int left = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
bool textUpdated = false;
|
||||
};
|
||||
explicit SendActionManager();
|
||||
|
||||
void registerFor(
|
||||
not_null<History*> history,
|
||||
MsgId rootId,
|
||||
not_null<UserData*> user,
|
||||
const MTPSendMessageAction &action,
|
||||
TimeId when);
|
||||
|
||||
[[nodiscard]] auto animationUpdated() const
|
||||
-> rpl::producer<AnimationUpdate>;
|
||||
void updateAnimation(AnimationUpdate &&update);
|
||||
[[nodiscard]] auto speakingAnimationUpdated() const
|
||||
-> rpl::producer<not_null<History*>>;
|
||||
void updateSpeakingAnimation(not_null<History*> history);
|
||||
|
||||
using SendActionPainter = HistoryView::SendActionPainter;
|
||||
[[nodiscard]] std::shared_ptr<SendActionPainter> repliesPainter(
|
||||
not_null<History*> history,
|
||||
MsgId rootId);
|
||||
void repliesPainterRemoved(
|
||||
not_null<History*> history,
|
||||
MsgId rootId);
|
||||
void repliesPaintersClear(
|
||||
not_null<History*> history,
|
||||
not_null<UserData*> user);
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
bool callback(crl::time now);
|
||||
[[nodiscard]] SendActionPainter *lookupPainter(
|
||||
not_null<History*> history,
|
||||
MsgId rootId);
|
||||
|
||||
// When typing in this history started.
|
||||
base::flat_map<
|
||||
std::pair<not_null<History*>, MsgId>,
|
||||
crl::time> _sendActions;
|
||||
Ui::Animations::Basic _animation;
|
||||
|
||||
rpl::event_stream<AnimationUpdate> _animationUpdate;
|
||||
rpl::event_stream<not_null<History*>> _speakingAnimationUpdate;
|
||||
|
||||
base::flat_map<
|
||||
not_null<History*>,
|
||||
base::flat_map<
|
||||
MsgId,
|
||||
std::weak_ptr<SendActionPainter>>> _painters;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Data
|
||||
@@ -27,7 +27,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/view/media/history_view_media.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_send_action.h"
|
||||
#include "inline_bots/inline_bot_layout_item.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "storage/storage_encrypted_file.h"
|
||||
@@ -53,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_poll.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_scheduled_messages.h"
|
||||
#include "data/data_send_action.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "data/data_streaming.h"
|
||||
#include "data/data_media_rotation.h"
|
||||
@@ -229,15 +229,13 @@ Session::Session(not_null<Main::Session*> session)
|
||||
, _contactsNoChatsList(Dialogs::SortMode::Name)
|
||||
, _ttlCheckTimer([=] { checkTTLs(); })
|
||||
, _selfDestructTimer([=] { checkSelfDestructItems(); })
|
||||
, _sendActionsAnimation([=](crl::time now) {
|
||||
return sendActionsAnimationCallback(now);
|
||||
})
|
||||
, _pollsClosingTimer([=] { checkPollsClosings(); })
|
||||
, _unmuteByFinishedTimer([=] { unmuteByFinished(); })
|
||||
, _groups(this)
|
||||
, _chatsFilters(std::make_unique<ChatFilters>(this))
|
||||
, _scheduledMessages(std::make_unique<ScheduledMessages>(this))
|
||||
, _cloudThemes(std::make_unique<CloudThemes>(session))
|
||||
, _sendActionManager(std::make_unique<SendActionManager>())
|
||||
, _streaming(std::make_unique<Streaming>(this))
|
||||
, _mediaRotation(std::make_unique<MediaRotation>())
|
||||
, _histories(std::make_unique<Histories>(this))
|
||||
@@ -278,7 +276,7 @@ void Session::clear() {
|
||||
// Optimization: clear notifications before destroying items.
|
||||
Core::App().notifications().clearFromSession(_session);
|
||||
|
||||
_sendActions.clear();
|
||||
_sendActionManager->clear();
|
||||
|
||||
_histories->unloadAll();
|
||||
_scheduledMessages = nullptr;
|
||||
@@ -1000,117 +998,6 @@ void Session::cancelForwarding(not_null<History*> history) {
|
||||
Data::HistoryUpdate::Flag::ForwardDraft);
|
||||
}
|
||||
|
||||
HistoryView::SendActionPainter *Session::lookupSendActionPainter(
|
||||
not_null<History*> history,
|
||||
MsgId rootId) {
|
||||
if (!rootId) {
|
||||
return history->sendActionPainter();
|
||||
}
|
||||
const auto i = _sendActionPainters.find(history);
|
||||
if (i == end(_sendActionPainters)) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto j = i->second.find(rootId);
|
||||
if (j == end(i->second)) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto result = j->second.lock();
|
||||
if (!result) {
|
||||
i->second.erase(j);
|
||||
if (i->second.empty()) {
|
||||
_sendActionPainters.erase(i);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
crl::on_main([copy = result] {
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
|
||||
void Session::registerSendAction(
|
||||
not_null<History*> history,
|
||||
MsgId rootId,
|
||||
not_null<UserData*> user,
|
||||
const MTPSendMessageAction &action,
|
||||
TimeId when) {
|
||||
if (history->peer->isSelf()) {
|
||||
return;
|
||||
}
|
||||
const auto sendAction = lookupSendActionPainter(history, rootId);
|
||||
if (!sendAction) {
|
||||
return;
|
||||
}
|
||||
if (sendAction->updateNeedsAnimating(user, action)) {
|
||||
user->madeAction(when);
|
||||
|
||||
if (!_sendActions.contains(std::pair{ history, rootId })) {
|
||||
_sendActions.emplace(std::pair{ history, rootId }, crl::now());
|
||||
_sendActionsAnimation.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Session::repliesSendActionPainter(
|
||||
not_null<History*> history,
|
||||
MsgId rootId)
|
||||
-> std::shared_ptr<SendActionPainter> {
|
||||
auto &weak = _sendActionPainters[history][rootId];
|
||||
if (auto strong = weak.lock()) {
|
||||
return strong;
|
||||
}
|
||||
auto result = std::make_shared<SendActionPainter>(history);
|
||||
weak = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Session::repliesSendActionPainterRemoved(
|
||||
not_null<History*> history,
|
||||
MsgId rootId) {
|
||||
const auto i = _sendActionPainters.find(history);
|
||||
if (i == end(_sendActionPainters)) {
|
||||
return;
|
||||
}
|
||||
const auto j = i->second.find(rootId);
|
||||
if (j == end(i->second) || j->second.lock()) {
|
||||
return;
|
||||
}
|
||||
i->second.erase(j);
|
||||
if (i->second.empty()) {
|
||||
_sendActionPainters.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
void Session::repliesSendActionPaintersClear(
|
||||
not_null<History*> history,
|
||||
not_null<UserData*> user) {
|
||||
auto &map = _sendActionPainters[history];
|
||||
for (auto i = map.begin(); i != map.end();) {
|
||||
if (auto strong = i->second.lock()) {
|
||||
strong->clear(user);
|
||||
++i;
|
||||
} else {
|
||||
i = map.erase(i);
|
||||
}
|
||||
}
|
||||
if (map.empty()) {
|
||||
_sendActionPainters.erase(history);
|
||||
}
|
||||
}
|
||||
|
||||
bool Session::sendActionsAnimationCallback(crl::time now) {
|
||||
for (auto i = begin(_sendActions); i != end(_sendActions);) {
|
||||
const auto sendAction = lookupSendActionPainter(
|
||||
i->first.first,
|
||||
i->first.second);
|
||||
if (sendAction && sendAction->updateNeedsAnimating(now)) {
|
||||
++i;
|
||||
} else {
|
||||
i = _sendActions.erase(i);
|
||||
}
|
||||
}
|
||||
return !_sendActions.empty();
|
||||
}
|
||||
|
||||
bool Session::chatsListLoaded(Data::Folder *folder) {
|
||||
return chatsList(folder)->loaded();
|
||||
}
|
||||
@@ -2301,25 +2188,6 @@ HistoryItem *Session::addNewMessage(
|
||||
return result;
|
||||
}
|
||||
|
||||
auto Session::sendActionAnimationUpdated() const
|
||||
-> rpl::producer<SendActionAnimationUpdate> {
|
||||
return _sendActionAnimationUpdate.events();
|
||||
}
|
||||
|
||||
void Session::updateSendActionAnimation(
|
||||
SendActionAnimationUpdate &&update) {
|
||||
_sendActionAnimationUpdate.fire(std::move(update));
|
||||
}
|
||||
|
||||
auto Session::speakingAnimationUpdated() const
|
||||
-> rpl::producer<not_null<History*>> {
|
||||
return _speakingAnimationUpdate.events();
|
||||
}
|
||||
|
||||
void Session::updateSpeakingAnimation(not_null<History*> history) {
|
||||
_speakingAnimationUpdate.fire_copy(history);
|
||||
}
|
||||
|
||||
int Session::unreadBadge() const {
|
||||
return computeUnreadBadge(_chatsList.unreadState());
|
||||
}
|
||||
@@ -2360,6 +2228,23 @@ void Session::notifyUnreadBadgeChanged() {
|
||||
_unreadBadgeChanges.fire({});
|
||||
}
|
||||
|
||||
std::optional<int> Session::countUnreadRepliesLocally(
|
||||
not_null<HistoryItem*> root,
|
||||
MsgId afterId) const {
|
||||
auto result = std::optional<int>();
|
||||
_unreadRepliesCountRequests.fire({
|
||||
.root = root,
|
||||
.afterId = afterId,
|
||||
.result = &result,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
auto Session::unreadRepliesCountRequests() const
|
||||
-> rpl::producer<UnreadRepliesCountRequest> {
|
||||
return _unreadRepliesCountRequests.events();
|
||||
}
|
||||
|
||||
int Session::computeUnreadBadge(const Dialogs::UnreadState &state) const {
|
||||
const auto all = Core::App().settings().includeMutedCounter();
|
||||
return std::max(state.marks - (all ? 0 : state.marksMuted), 0)
|
||||
@@ -2619,13 +2504,14 @@ void Session::photoApplyFields(
|
||||
}
|
||||
const auto area = [](const MTPVideoSize &size) {
|
||||
return size.match([](const MTPDvideoSize &data) {
|
||||
return data.vw().v * data.vh().v;
|
||||
return data.vsize().v ? (data.vw().v * data.vh().v) : 0;
|
||||
});
|
||||
};
|
||||
return *ranges::max_element(
|
||||
const auto result = *ranges::max_element(
|
||||
sizes->v,
|
||||
std::greater<>(),
|
||||
area);
|
||||
return (area(result) > 0) ? std::make_optional(result) : std::nullopt;
|
||||
};
|
||||
const auto useProgressive = (progressive != sizes.end());
|
||||
const auto large = useProgressive
|
||||
@@ -4168,10 +4054,18 @@ void Session::setWallpapers(const QVector<MTPWallPaper> &data, int32 hash) {
|
||||
ranges::rotate(begin(_wallpapers), legacy2, legacy2 + 1);
|
||||
}
|
||||
|
||||
// Put the legacy3 (static gradient) wallpaper to the front of the list.
|
||||
const auto legacy3 = ranges::find_if(
|
||||
_wallpapers,
|
||||
Data::IsLegacy3DefaultWallPaper);
|
||||
if (legacy3 != end(_wallpapers)) {
|
||||
ranges::rotate(begin(_wallpapers), legacy3, legacy3 + 1);
|
||||
}
|
||||
|
||||
if (ranges::none_of(_wallpapers, Data::IsDefaultWallPaper)) {
|
||||
_wallpapers.push_back(Data::DefaultWallPaper());
|
||||
_wallpapers.back().setLocalImageAsThumbnail(std::make_shared<Image>(
|
||||
u":/gui/art/background.jpg"_q));
|
||||
u":/gui/art/bg_thumbnail.png"_q));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_location_manager.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/flags.h"
|
||||
#include "ui/effects/animations.h"
|
||||
|
||||
class Image;
|
||||
class HistoryItem;
|
||||
@@ -31,7 +30,6 @@ namespace HistoryView {
|
||||
struct Group;
|
||||
class Element;
|
||||
class ElementDelegate;
|
||||
class SendActionPainter;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace Main {
|
||||
@@ -52,6 +50,7 @@ class Folder;
|
||||
class LocationPoint;
|
||||
class WallPaper;
|
||||
class ScheduledMessages;
|
||||
class SendActionManager;
|
||||
class ChatFilters;
|
||||
class CloudThemes;
|
||||
class Streaming;
|
||||
@@ -92,6 +91,9 @@ public:
|
||||
[[nodiscard]] ScheduledMessages &scheduledMessages() const {
|
||||
return *_scheduledMessages;
|
||||
}
|
||||
[[nodiscard]] SendActionManager &sendActionManager() const {
|
||||
return *_sendActionManager;
|
||||
}
|
||||
[[nodiscard]] CloudThemes &cloudThemes() const {
|
||||
return *_cloudThemes;
|
||||
}
|
||||
@@ -192,13 +194,6 @@ public:
|
||||
|
||||
void cancelForwarding(not_null<History*> history);
|
||||
|
||||
void registerSendAction(
|
||||
not_null<History*> history,
|
||||
MsgId rootId,
|
||||
not_null<UserData*> user,
|
||||
const MTPSendMessageAction &action,
|
||||
TimeId when);
|
||||
|
||||
[[nodiscard]] rpl::variable<bool> &contactsLoaded() {
|
||||
return _contactsLoaded;
|
||||
}
|
||||
@@ -403,38 +398,26 @@ public:
|
||||
MessageFlags localFlags,
|
||||
NewMessageType type);
|
||||
|
||||
struct SendActionAnimationUpdate {
|
||||
not_null<History*> history;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
bool textUpdated = false;
|
||||
};
|
||||
[[nodiscard]] auto sendActionAnimationUpdated() const
|
||||
-> rpl::producer<SendActionAnimationUpdate>;
|
||||
void updateSendActionAnimation(SendActionAnimationUpdate &&update);
|
||||
[[nodiscard]] auto speakingAnimationUpdated() const
|
||||
-> rpl::producer<not_null<History*>>;
|
||||
void updateSpeakingAnimation(not_null<History*> history);
|
||||
|
||||
using SendActionPainter = HistoryView::SendActionPainter;
|
||||
[[nodiscard]] std::shared_ptr<SendActionPainter> repliesSendActionPainter(
|
||||
not_null<History*> history,
|
||||
MsgId rootId);
|
||||
void repliesSendActionPainterRemoved(
|
||||
not_null<History*> history,
|
||||
MsgId rootId);
|
||||
void repliesSendActionPaintersClear(
|
||||
not_null<History*> history,
|
||||
not_null<UserData*> user);
|
||||
|
||||
[[nodiscard]] int unreadBadge() const;
|
||||
[[nodiscard]] bool unreadBadgeMuted() const;
|
||||
[[nodiscard]] int unreadBadgeIgnoreOne(const Dialogs::Key &key) const;
|
||||
[[nodiscard]] bool unreadBadgeMutedIgnoreOne(const Dialogs::Key &key) const;
|
||||
[[nodiscard]] bool unreadBadgeMutedIgnoreOne(
|
||||
const Dialogs::Key &key) const;
|
||||
[[nodiscard]] int unreadOnlyMutedBadge() const;
|
||||
[[nodiscard]] rpl::producer<> unreadBadgeChanges() const;
|
||||
void notifyUnreadBadgeChanged();
|
||||
|
||||
[[nodiscard]] std::optional<int> countUnreadRepliesLocally(
|
||||
not_null<HistoryItem*> root,
|
||||
MsgId afterId) const;
|
||||
struct UnreadRepliesCountRequest {
|
||||
not_null<HistoryItem*> root;
|
||||
MsgId afterId = 0;
|
||||
not_null<std::optional<int>*> result;
|
||||
};
|
||||
[[nodiscard]] auto unreadRepliesCountRequests() const
|
||||
-> rpl::producer<UnreadRepliesCountRequest>;
|
||||
|
||||
void selfDestructIn(not_null<HistoryItem*> item, crl::time delay);
|
||||
|
||||
[[nodiscard]] not_null<PhotoData*> photo(PhotoId id);
|
||||
@@ -810,11 +793,6 @@ private:
|
||||
const MTPMessageMedia &media,
|
||||
TimeId date);
|
||||
|
||||
bool sendActionsAnimationCallback(crl::time now);
|
||||
[[nodiscard]] SendActionPainter *lookupSendActionPainter(
|
||||
not_null<History*> history,
|
||||
MsgId rootId);
|
||||
|
||||
void setWallpapers(const QVector<MTPWallPaper> &data, int32 hash);
|
||||
|
||||
void checkPollsClosings();
|
||||
@@ -855,6 +833,7 @@ private:
|
||||
rpl::event_stream<DialogsRowReplacement> _dialogsRowReplacements;
|
||||
rpl::event_stream<ChatListEntryRefresh> _chatListEntryRefreshes;
|
||||
rpl::event_stream<> _unreadBadgeChanges;
|
||||
rpl::event_stream<UnreadRepliesCountRequest> _unreadRepliesCountRequests;
|
||||
|
||||
Dialogs::MainList _chatsList;
|
||||
Dialogs::IndexedList _contactsList;
|
||||
@@ -875,12 +854,6 @@ private:
|
||||
base::Timer _selfDestructTimer;
|
||||
std::vector<FullMsgId> _selfDestructItems;
|
||||
|
||||
// When typing in this history started.
|
||||
base::flat_map<
|
||||
std::pair<not_null<History*>, MsgId>,
|
||||
crl::time> _sendActions;
|
||||
Ui::Animations::Basic _sendActionsAnimation;
|
||||
|
||||
std::unordered_map<
|
||||
PhotoId,
|
||||
std::unique_ptr<PhotoData>> _photos;
|
||||
@@ -970,9 +943,6 @@ private:
|
||||
int>;
|
||||
std::unique_ptr<CredentialsWithGeneration> _passportCredentials;
|
||||
|
||||
rpl::event_stream<SendActionAnimationUpdate> _sendActionAnimationUpdate;
|
||||
rpl::event_stream<not_null<History*>> _speakingAnimationUpdate;
|
||||
|
||||
std::vector<WallPaper> _wallpapers;
|
||||
int32 _wallpapersHash = 0;
|
||||
|
||||
@@ -980,14 +950,10 @@ private:
|
||||
std::unique_ptr<ChatFilters> _chatsFilters;
|
||||
std::unique_ptr<ScheduledMessages> _scheduledMessages;
|
||||
std::unique_ptr<CloudThemes> _cloudThemes;
|
||||
std::unique_ptr<SendActionManager> _sendActionManager;
|
||||
std::unique_ptr<Streaming> _streaming;
|
||||
std::unique_ptr<MediaRotation> _mediaRotation;
|
||||
std::unique_ptr<Histories> _histories;
|
||||
base::flat_map<
|
||||
not_null<History*>,
|
||||
base::flat_map<
|
||||
MsgId,
|
||||
std::weak_ptr<SendActionPainter>>> _sendActionPainters;
|
||||
std::unique_ptr<Stickers> _stickers;
|
||||
MsgId _nonHistoryEntryId = ServerMaxMsgId;
|
||||
|
||||
|
||||
@@ -7,12 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_types.h"
|
||||
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "storage/cache/storage_cache_types.h"
|
||||
#include "base/openssl_help.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Data {
|
||||
namespace {
|
||||
@@ -90,30 +87,6 @@ Storage::Cache::Key GeoPointCacheKey(const GeoPointLocation &location) {
|
||||
|
||||
} // namespace Data
|
||||
|
||||
uint32 AudioMsgId::CreateExternalPlayId() {
|
||||
static auto Result = uint32(0);
|
||||
return ++Result ? Result : ++Result;
|
||||
}
|
||||
|
||||
AudioMsgId AudioMsgId::ForVideo() {
|
||||
auto result = AudioMsgId();
|
||||
result._externalPlayId = CreateExternalPlayId();
|
||||
result._type = Type::Video;
|
||||
return result;
|
||||
}
|
||||
|
||||
void AudioMsgId::setTypeFromAudio() {
|
||||
if (_audio->isVoiceMessage() || _audio->isVideoMessage()) {
|
||||
_type = Type::Voice;
|
||||
} else if (_audio->isVideoFile()) {
|
||||
_type = Type::Video;
|
||||
} else if (_audio->isAudioFile()) {
|
||||
_type = Type::Song;
|
||||
} else {
|
||||
_type = Type::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageCursor::fillFrom(not_null<const Ui::InputField*> field) {
|
||||
const auto cursor = field->textCursor();
|
||||
position = cursor.position();
|
||||
|
||||
@@ -28,10 +28,6 @@ namespace Ui {
|
||||
class InputField;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Images {
|
||||
enum class Option;
|
||||
using Options = base::flags<Option>;
|
||||
@@ -192,8 +188,6 @@ struct WebPageData;
|
||||
struct GameData;
|
||||
struct PollData;
|
||||
|
||||
class AudioMsgId;
|
||||
|
||||
using PhotoId = uint64;
|
||||
using VideoId = uint64;
|
||||
using AudioId = uint64;
|
||||
@@ -246,78 +240,6 @@ inline constexpr auto kStickerSideSize = 512;
|
||||
|
||||
using MediaKey = QPair<uint64, uint64>;
|
||||
|
||||
class AudioMsgId {
|
||||
public:
|
||||
enum class Type {
|
||||
Unknown,
|
||||
Voice,
|
||||
Song,
|
||||
Video,
|
||||
};
|
||||
|
||||
AudioMsgId() = default;
|
||||
AudioMsgId(
|
||||
not_null<DocumentData*> audio,
|
||||
FullMsgId msgId,
|
||||
uint32 externalPlayId = 0)
|
||||
: _audio(audio)
|
||||
, _contextId(msgId)
|
||||
, _externalPlayId(externalPlayId) {
|
||||
setTypeFromAudio();
|
||||
}
|
||||
|
||||
[[nodiscard]] static uint32 CreateExternalPlayId();
|
||||
[[nodiscard]] static AudioMsgId ForVideo();
|
||||
|
||||
[[nodiscard]] Type type() const {
|
||||
return _type;
|
||||
}
|
||||
[[nodiscard]] DocumentData *audio() const {
|
||||
return _audio;
|
||||
}
|
||||
[[nodiscard]] FullMsgId contextId() const {
|
||||
return _contextId;
|
||||
}
|
||||
[[nodiscard]] uint32 externalPlayId() const {
|
||||
return _externalPlayId;
|
||||
}
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return (_audio != nullptr) || (_externalPlayId != 0);
|
||||
}
|
||||
|
||||
private:
|
||||
void setTypeFromAudio();
|
||||
|
||||
DocumentData *_audio = nullptr;
|
||||
Type _type = Type::Unknown;
|
||||
FullMsgId _contextId;
|
||||
uint32 _externalPlayId = 0;
|
||||
|
||||
};
|
||||
|
||||
inline bool operator<(const AudioMsgId &a, const AudioMsgId &b) {
|
||||
if (quintptr(a.audio()) < quintptr(b.audio())) {
|
||||
return true;
|
||||
} else if (quintptr(b.audio()) < quintptr(a.audio())) {
|
||||
return false;
|
||||
} else if (a.contextId() < b.contextId()) {
|
||||
return true;
|
||||
} else if (b.contextId() < a.contextId()) {
|
||||
return false;
|
||||
}
|
||||
return (a.externalPlayId() < b.externalPlayId());
|
||||
}
|
||||
|
||||
inline bool operator==(const AudioMsgId &a, const AudioMsgId &b) {
|
||||
return (a.audio() == b.audio())
|
||||
&& (a.contextId() == b.contextId())
|
||||
&& (a.externalPlayId() == b.externalPlayId());
|
||||
}
|
||||
|
||||
inline bool operator!=(const AudioMsgId &a, const AudioMsgId &b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
struct MessageCursor {
|
||||
MessageCursor() = default;
|
||||
MessageCursor(int position, int anchor, int scroll)
|
||||
|
||||
@@ -225,6 +225,7 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
|
||||
user->setAbout(qs(update.vabout().value_or_empty()));
|
||||
user->setCommonChatsCount(update.vcommon_chats_count().v);
|
||||
user->checkFolder(update.vfolder_id().value_or_empty());
|
||||
user->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
|
||||
user->fullUpdated();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_session.h"
|
||||
#include "storage/serialize_common.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "core/application.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
@@ -29,7 +30,9 @@ constexpr auto kThemeBackground = FromLegacyBackgroundId(-2);
|
||||
constexpr auto kCustomBackground = FromLegacyBackgroundId(-1);
|
||||
constexpr auto kLegacy1DefaultBackground = FromLegacyBackgroundId(0);
|
||||
constexpr auto kLegacy2DefaultBackground = 5947530738516623361;
|
||||
constexpr auto kDefaultBackground = 5778236420632084488;
|
||||
constexpr auto kLegacy3DefaultBackground = 5778236420632084488;
|
||||
constexpr auto kLegacy4DefaultBackground = 5945087215657811969;
|
||||
constexpr auto kDefaultBackground = 5933856211186221059;
|
||||
constexpr auto kIncorrectDefaultBackground = FromLegacyBackgroundId(105);
|
||||
|
||||
constexpr auto kVersionTag = qint32(0x7FFFFFFF);
|
||||
@@ -45,25 +48,6 @@ constexpr auto kVersion = 1;
|
||||
return color ? SerializeColor(*color) : quint32(-1);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<QColor> MaybeColorFromSerialized(
|
||||
quint32 serialized) {
|
||||
return (serialized == quint32(-1))
|
||||
? std::nullopt
|
||||
: std::make_optional(QColor(
|
||||
int((serialized >> 16) & 0xFFU),
|
||||
int((serialized >> 8) & 0xFFU),
|
||||
int(serialized & 0xFFU)));
|
||||
}
|
||||
|
||||
[[nodiscard]] QColor DefaultBackgroundColor() {
|
||||
return QColor(213, 223, 233);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<QColor> MaybeColorFromSerialized(
|
||||
const tl::conditional<MTPint> &mtp) {
|
||||
return mtp ? MaybeColorFromSerialized(mtp->v) : std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<QColor> ColorsFromMTP(
|
||||
const MTPDwallPaperSettings &data) {
|
||||
auto result = std::vector<QColor>();
|
||||
@@ -196,12 +180,6 @@ WallPaperId WallPaper::id() const {
|
||||
return _id;
|
||||
}
|
||||
|
||||
std::optional<QColor> WallPaper::backgroundColor() const {
|
||||
return _backgroundColors.empty()
|
||||
? std::nullopt
|
||||
: std::make_optional(_backgroundColors.front());
|
||||
}
|
||||
|
||||
const std::vector<QColor> WallPaper::backgroundColors() const {
|
||||
return _backgroundColors;
|
||||
}
|
||||
@@ -412,13 +390,16 @@ WallPaper WallPaper::withBackgroundColors(std::vector<QColor> colors) const {
|
||||
WallPaper WallPaper::withParamsFrom(const WallPaper &other) const {
|
||||
auto result = *this;
|
||||
result._blurred = other._blurred;
|
||||
if (!other._backgroundColors.empty() || ColorsFromString(_slug).empty()) {
|
||||
if (!other._backgroundColors.empty()) {
|
||||
result._backgroundColors = other._backgroundColors;
|
||||
if (!ColorsFromString(_slug).empty()) {
|
||||
result._slug = StringFromColors(result._backgroundColors);
|
||||
}
|
||||
}
|
||||
result._intensity = other._intensity;
|
||||
if (other.isPattern()) {
|
||||
result._flags |= WallPaperFlag::Pattern;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -656,6 +637,19 @@ std::optional<WallPaper> WallPaper::FromColorsSlug(const QString &slug) {
|
||||
return result;
|
||||
}
|
||||
|
||||
WallPaper WallPaper::ConstructDefault() {
|
||||
auto result = WallPaper(
|
||||
kDefaultBackground
|
||||
).withPatternIntensity(50).withBackgroundColors({
|
||||
QColor(219, 221, 187),
|
||||
QColor(107, 165, 135),
|
||||
QColor(213, 216, 141),
|
||||
QColor(136, 184, 132),
|
||||
});
|
||||
result._flags |= WallPaperFlag::Default | WallPaperFlag::Pattern;
|
||||
return result;
|
||||
}
|
||||
|
||||
WallPaper ThemeWallPaper() {
|
||||
return WallPaper(kThemeBackground);
|
||||
}
|
||||
@@ -685,8 +679,16 @@ bool IsLegacy2DefaultWallPaper(const WallPaper &paper) {
|
||||
|| (paper.id() == kIncorrectDefaultBackground);
|
||||
}
|
||||
|
||||
bool IsLegacy3DefaultWallPaper(const WallPaper &paper) {
|
||||
return (paper.id() == kLegacy3DefaultBackground);
|
||||
}
|
||||
|
||||
bool IsLegacy4DefaultWallPaper(const WallPaper &paper) {
|
||||
return (paper.id() == kLegacy4DefaultBackground);
|
||||
}
|
||||
|
||||
WallPaper DefaultWallPaper() {
|
||||
return WallPaper(kDefaultBackground);
|
||||
return WallPaper::ConstructDefault();
|
||||
}
|
||||
|
||||
bool IsDefaultWallPaper(const WallPaper &paper) {
|
||||
@@ -704,93 +706,35 @@ bool IsCloudWallPaper(const WallPaper &paper) {
|
||||
&& !details::IsTestingEditorWallPaper(paper);
|
||||
}
|
||||
|
||||
QImage GenerateWallPaper(
|
||||
QSize size,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity,
|
||||
Fn<void(QPainter&)> drawPattern) {
|
||||
auto result = bg.empty()
|
||||
? Images::GenerateGradient(size, { DefaultBackgroundColor() })
|
||||
: Images::GenerateGradient(size, bg, gradientRotation);
|
||||
if (bg.size() > 1 && (!drawPattern || patternOpacity >= 0.)) {
|
||||
result = Images::DitherImage(std::move(result));
|
||||
}
|
||||
if (drawPattern) {
|
||||
auto p = QPainter(&result);
|
||||
if (patternOpacity >= 0.) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(patternOpacity);
|
||||
} else {
|
||||
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||
}
|
||||
drawPattern(p);
|
||||
if (patternOpacity < 0. && patternOpacity > -1.) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
p.setOpacity(1. + patternOpacity);
|
||||
p.fillRect(QRect{ QPoint(), size }, Qt::black);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(result).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
|
||||
QImage PreparePatternImage(
|
||||
QImage pattern,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity) {
|
||||
auto result = GenerateWallPaper(
|
||||
pattern.size(),
|
||||
bg,
|
||||
gradientRotation,
|
||||
patternOpacity,
|
||||
[&](QPainter &p) {
|
||||
p.drawImage(QRect(QPoint(), pattern.size()), pattern);
|
||||
});
|
||||
|
||||
pattern = QImage();
|
||||
return result;
|
||||
}
|
||||
|
||||
QImage PrepareBlurredBackground(QImage image) {
|
||||
constexpr auto kSize = 900;
|
||||
constexpr auto kRadius = 24;
|
||||
if (image.width() > kSize || image.height() > kSize) {
|
||||
image = image.scaled(
|
||||
kSize,
|
||||
kSize,
|
||||
Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
}
|
||||
return Images::BlurLargeImage(image, kRadius);
|
||||
}
|
||||
|
||||
QImage GenerateDitheredGradient(
|
||||
const std::vector<QColor> &colors,
|
||||
int rotation) {
|
||||
constexpr auto kSize = 512;
|
||||
const auto size = QSize(kSize, kSize);
|
||||
if (colors.empty()) {
|
||||
return Images::GenerateGradient(size, { DefaultBackgroundColor() });
|
||||
}
|
||||
auto result = Images::GenerateGradient(size, colors, rotation);
|
||||
if (colors.size() > 1) {
|
||||
result = Images::DitherImage(std::move(result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QImage GenerateDitheredGradient(const Data::WallPaper &paper) {
|
||||
if (paper.backgroundColors().empty()) {
|
||||
return GenerateDitheredGradient({ DefaultBackgroundColor() }, 0);
|
||||
}
|
||||
return GenerateDitheredGradient(
|
||||
return Ui::GenerateDitheredGradient(
|
||||
paper.backgroundColors(),
|
||||
paper.gradientRotation());
|
||||
}
|
||||
|
||||
QColor ColorFromSerialized(quint32 serialized) {
|
||||
return QColor(
|
||||
int((serialized >> 16) & 0xFFU),
|
||||
int((serialized >> 8) & 0xFFU),
|
||||
int(serialized & 0xFFU));
|
||||
}
|
||||
|
||||
QColor ColorFromSerialized(MTPint serialized) {
|
||||
return ColorFromSerialized(serialized.v);
|
||||
}
|
||||
|
||||
std::optional<QColor> MaybeColorFromSerialized(
|
||||
quint32 serialized) {
|
||||
return (serialized == quint32(-1))
|
||||
? std::nullopt
|
||||
: std::make_optional(ColorFromSerialized(serialized));
|
||||
}
|
||||
|
||||
std::optional<QColor> MaybeColorFromSerialized(
|
||||
const tl::conditional<MTPint> &mtp) {
|
||||
return mtp ? std::make_optional(ColorFromSerialized(*mtp)) : std::nullopt;
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
WallPaper UninitializedWallPaper() {
|
||||
@@ -810,7 +754,9 @@ bool IsTestingThemeWallPaper(const WallPaper &paper) {
|
||||
}
|
||||
|
||||
WallPaper TestingDefaultWallPaper() {
|
||||
return WallPaper(kTestingDefaultBackground);
|
||||
return WallPaper(
|
||||
kTestingDefaultBackground
|
||||
).withParamsFrom(DefaultWallPaper());
|
||||
}
|
||||
|
||||
bool IsTestingDefaultWallPaper(const WallPaper &paper) {
|
||||
|
||||
@@ -35,7 +35,6 @@ public:
|
||||
void setLocalImageAsThumbnail(std::shared_ptr<Image> image);
|
||||
|
||||
[[nodiscard]] WallPaperId id() const;
|
||||
[[nodiscard]] std::optional<QColor> backgroundColor() const;
|
||||
[[nodiscard]] const std::vector<QColor> backgroundColors() const;
|
||||
[[nodiscard]] DocumentData *document() const;
|
||||
[[nodiscard]] Image *localThumbnail() const;
|
||||
@@ -91,6 +90,7 @@ public:
|
||||
qint32 legacyId);
|
||||
[[nodiscard]] static std::optional<WallPaper> FromColorsSlug(
|
||||
const QString &slug);
|
||||
[[nodiscard]] static WallPaper ConstructDefault();
|
||||
|
||||
private:
|
||||
static constexpr auto kDefaultIntensity = 50;
|
||||
@@ -118,27 +118,21 @@ private:
|
||||
[[nodiscard]] WallPaper Legacy1DefaultWallPaper();
|
||||
[[nodiscard]] bool IsLegacy1DefaultWallPaper(const WallPaper &paper);
|
||||
[[nodiscard]] bool IsLegacy2DefaultWallPaper(const WallPaper &paper);
|
||||
[[nodiscard]] bool IsLegacy3DefaultWallPaper(const WallPaper &paper);
|
||||
[[nodiscard]] bool IsLegacy4DefaultWallPaper(const WallPaper &paper);
|
||||
[[nodiscard]] WallPaper DefaultWallPaper();
|
||||
[[nodiscard]] bool IsDefaultWallPaper(const WallPaper &paper);
|
||||
[[nodiscard]] bool IsCloudWallPaper(const WallPaper &paper);
|
||||
|
||||
[[nodiscard]] QImage GenerateWallPaper(
|
||||
QSize size,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity = 1.,
|
||||
Fn<void(QPainter&)> drawPattern = nullptr);
|
||||
[[nodiscard]] QImage PreparePatternImage(
|
||||
QImage pattern,
|
||||
const std::vector<QColor> &bg,
|
||||
int gradientRotation,
|
||||
float64 patternOpacity);
|
||||
[[nodiscard]] QImage PrepareBlurredBackground(QImage image);
|
||||
[[nodiscard]] QImage GenerateDitheredGradient(
|
||||
const std::vector<QColor> &colors,
|
||||
int rotation);
|
||||
[[nodiscard]] QImage GenerateDitheredGradient(const WallPaper &paper);
|
||||
|
||||
[[nodiscard]] QColor ColorFromSerialized(quint32 serialized);
|
||||
[[nodiscard]] QColor ColorFromSerialized(MTPint serialized);
|
||||
[[nodiscard]] std::optional<QColor> MaybeColorFromSerialized(
|
||||
quint32 serialized);
|
||||
[[nodiscard]] std::optional<QColor> MaybeColorFromSerialized(
|
||||
const tl::conditional<MTPint> &mtp);
|
||||
|
||||
namespace details {
|
||||
|
||||
[[nodiscard]] WallPaper UninitializedWallPaper();
|
||||
|
||||
@@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_cloud_file.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_send_action.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwindow.h"
|
||||
@@ -166,11 +167,12 @@ InnerWidget::InnerWidget(
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
session().data().sendActionAnimationUpdated(
|
||||
session().data().sendActionManager().animationUpdated(
|
||||
) | rpl::start_with_next([=](
|
||||
const Data::Session::SendActionAnimationUpdate &update) {
|
||||
const Data::SendActionManager::AnimationUpdate &update) {
|
||||
using RowPainter = Layout::RowPainter;
|
||||
const auto updateRect = RowPainter::sendActionAnimationRect(
|
||||
update.left,
|
||||
update.width,
|
||||
update.height,
|
||||
width(),
|
||||
@@ -181,7 +183,7 @@ InnerWidget::InnerWidget(
|
||||
UpdateRowSection::Default | UpdateRowSection::Filtered);
|
||||
}, lifetime());
|
||||
|
||||
session().data().speakingAnimationUpdated(
|
||||
session().data().sendActionManager().speakingAnimationUpdated(
|
||||
) | rpl::start_with_next([=](not_null<History*> history) {
|
||||
updateDialogRowCornerStatus(history);
|
||||
}, lifetime());
|
||||
|
||||
@@ -943,11 +943,24 @@ void RowPainter::paint(
|
||||
paintCounterCallback);
|
||||
}
|
||||
|
||||
QRect RowPainter::sendActionAnimationRect(int animationWidth, int animationHeight, int fullWidth, bool textUpdated) {
|
||||
auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding;
|
||||
auto namewidth = fullWidth - nameleft - st::dialogsPadding.x();
|
||||
auto texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip;
|
||||
return QRect(nameleft, texttop, textUpdated ? namewidth : animationWidth, animationHeight);
|
||||
QRect RowPainter::sendActionAnimationRect(
|
||||
int animationLeft,
|
||||
int animationWidth,
|
||||
int animationHeight,
|
||||
int fullWidth,
|
||||
bool textUpdated) {
|
||||
const auto nameleft = st::dialogsPadding.x()
|
||||
+ st::dialogsPhotoSize
|
||||
+ st::dialogsPhotoPadding;
|
||||
const auto namewidth = fullWidth - nameleft - st::dialogsPadding.x();
|
||||
const auto texttop = st::dialogsPadding.y()
|
||||
+ st::msgNameFont->height
|
||||
+ st::dialogsSkip;
|
||||
return QRect(
|
||||
nameleft + (textUpdated ? 0 : animationLeft),
|
||||
texttop,
|
||||
textUpdated ? namewidth : animationWidth,
|
||||
animationHeight);
|
||||
}
|
||||
|
||||
void PaintCollapsedRow(
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
crl::time ms,
|
||||
bool displayUnreadInfo);
|
||||
static QRect sendActionAnimationRect(
|
||||
int animationLeft,
|
||||
int animationWidth,
|
||||
int animationHeight,
|
||||
int fullWidth,
|
||||
|
||||
@@ -164,7 +164,7 @@ void Widget::BottomButton::paintEvent(QPaintEvent *e) {
|
||||
Widget::Widget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: Window::AbstractSectionWidget(parent, controller, PaintedBackground::Custom)
|
||||
: Window::AbstractSectionWidget(parent, controller, nullptr)
|
||||
, _api(&controller->session().mtp())
|
||||
, _searchControls(this)
|
||||
, _mainMenuToggle(_searchControls, st::dialogsMenuToggle)
|
||||
|
||||
@@ -1123,6 +1123,10 @@ ServiceAction ParseServiceAction(
|
||||
result.content = ActionGroupCallScheduled{
|
||||
.date = data.vschedule_date().v,
|
||||
};
|
||||
}, [&](const MTPDmessageActionSetChatTheme &data) {
|
||||
result.content = ActionSetChatTheme{
|
||||
.emoji = qs(data.vemoticon()),
|
||||
};
|
||||
}, [](const MTPDmessageActionEmpty &data) {});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -475,6 +475,10 @@ struct ActionGroupCallScheduled {
|
||||
TimeId date = 0;
|
||||
};
|
||||
|
||||
struct ActionSetChatTheme {
|
||||
QString emoji;
|
||||
};
|
||||
|
||||
struct ServiceAction {
|
||||
std::variant<
|
||||
v::null_t,
|
||||
@@ -503,7 +507,8 @@ struct ServiceAction {
|
||||
ActionGroupCall,
|
||||
ActionInviteToGroupCall,
|
||||
ActionSetMessagesTTL,
|
||||
ActionGroupCallScheduled> content;
|
||||
ActionGroupCallScheduled,
|
||||
ActionSetChatTheme> content;
|
||||
};
|
||||
|
||||
ServiceAction ParseServiceAction(
|
||||
|
||||
@@ -1095,8 +1095,17 @@ auto HtmlWriter::Wrap::pushMessage(
|
||||
}, [&](const ActionGroupCallScheduled &data) {
|
||||
const auto dateText = FormatDateTime(data.date);
|
||||
return isChannel
|
||||
? "Voice chat scheduled for " + dateText
|
||||
? ("Voice chat scheduled for " + dateText)
|
||||
: (serviceFrom + " scheduled a voice chat for " + dateText);
|
||||
}, [&](const ActionSetChatTheme &data) {
|
||||
if (data.emoji.isEmpty()) {
|
||||
return isChannel
|
||||
? "Channel theme was disabled"
|
||||
: (serviceFrom + " disabled chat theme");
|
||||
}
|
||||
return isChannel
|
||||
? ("Channel theme was changed to " + data.emoji).toUtf8()
|
||||
: (serviceFrom + " changed chat theme to " + data.emoji).toUtf8();
|
||||
}, [](v::null_t) { return QByteArray(); });
|
||||
|
||||
if (!serviceText.isEmpty()) {
|
||||
|
||||
@@ -517,6 +517,12 @@ QByteArray SerializeMessage(
|
||||
pushActor();
|
||||
pushAction("group_call_scheduled");
|
||||
push("schedule_date", data.date);
|
||||
}, [&](const ActionSetChatTheme &data) {
|
||||
pushActor();
|
||||
pushAction("edit_chat_theme");
|
||||
if (!data.emoji.isEmpty()) {
|
||||
push("emoticon", data.emoji.toUtf8());
|
||||
}
|
||||
}, [](v::null_t) {});
|
||||
|
||||
if (v::is_null(message.action.content)) {
|
||||
|
||||
@@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class History;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
@@ -271,8 +271,10 @@ void FilterBox::Inner::createActionsCheckboxes(const FilterValue &filter) {
|
||||
addFlag(Flag::f_edit, tr::lng_admin_log_filter_messages_edited(tr::now));
|
||||
if (isGroup) {
|
||||
addFlag(Flag::f_pinned, tr::lng_admin_log_filter_messages_pinned(tr::now));
|
||||
addFlag(Flag::f_group_call, tr::lng_admin_log_filter_voice_chats(tr::now));
|
||||
} else {
|
||||
addFlag(Flag::f_group_call, tr::lng_admin_log_filter_voice_chats_channel(tr::now));
|
||||
}
|
||||
addFlag(Flag::f_group_call, tr::lng_admin_log_filter_voice_chats(tr::now));
|
||||
addFlag(Flag::f_invites, tr::lng_admin_log_filter_invite_links(tr::now));
|
||||
addFlag(Flag::f_leave, tr::lng_admin_log_filter_members_removed(tr::now));
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_session_controller.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
@@ -241,6 +242,14 @@ InnerWidget::InnerWidget(
|
||||
st::historyAdminLogEmptyWidth
|
||||
- st::historyAdminLogEmptyPadding.left()
|
||||
- st::historyAdminLogEmptyPadding.left()) {
|
||||
Window::ChatThemeValueFromPeer(
|
||||
controller,
|
||||
channel
|
||||
) | rpl::start_with_next([=](std::shared_ptr<Ui::ChatTheme> &&theme) {
|
||||
_theme = std::move(theme);
|
||||
controller->setChatStyleTheme(_theme);
|
||||
}, lifetime());
|
||||
|
||||
setMouseTracking(true);
|
||||
_scrollDateHideTimer.setCallback([=] { scrollDateHideByTimer(); });
|
||||
session().data().viewRepaintRequest(
|
||||
@@ -912,14 +921,13 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
return this->itemTop(elem) < bottom;
|
||||
});
|
||||
if (from != end) {
|
||||
auto viewport = QRect(); // #TODO bubbles
|
||||
auto top = itemTop(from->get());
|
||||
auto context = HistoryView::PaintContext{
|
||||
.bubblesPattern = nullptr,
|
||||
.viewport = viewport.translated(0, -top),
|
||||
.clip = clip.translated(0, -top),
|
||||
.now = crl::now(),
|
||||
};
|
||||
auto context = _controller->preparePaintContext({
|
||||
.theme = _theme.get(),
|
||||
.visibleAreaTop = _visibleTop,
|
||||
.visibleAreaTopGlobal = mapToGlobal(QPoint(0, _visibleTop)).y(),
|
||||
.clip = clip,
|
||||
}).translated(0, -top);
|
||||
p.translate(0, top);
|
||||
for (auto i = from; i != to; ++i) {
|
||||
const auto view = i->get();
|
||||
|
||||
@@ -58,6 +58,9 @@ public:
|
||||
not_null<ChannelData*> channel);
|
||||
|
||||
[[nodiscard]] Main::Session &session() const;
|
||||
[[nodiscard]] not_null<Ui::ChatTheme*> theme() const {
|
||||
return _theme.get();
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<> showSearchSignal() const;
|
||||
[[nodiscard]] rpl::producer<int> scrollToSignal() const;
|
||||
@@ -253,6 +256,7 @@ private:
|
||||
MTP::Sender _api;
|
||||
|
||||
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
||||
std::shared_ptr<Ui::ChatTheme> _theme;
|
||||
|
||||
std::vector<OwnedItem> _items;
|
||||
std::set<uint64> _eventIds;
|
||||
|
||||
@@ -165,10 +165,14 @@ TextWithEntities GenerateAdminChangeText(
|
||||
|
||||
auto result = tr::lng_admin_log_promoted(tr::now, lt_user, user, Ui::Text::WithEntities);
|
||||
|
||||
auto useInviteLinkPhrase = channel->isMegagroup() && channel->anyoneCanAddMembers();
|
||||
auto invitePhrase = useInviteLinkPhrase
|
||||
const auto useInviteLinkPhrase = channel->isMegagroup()
|
||||
&& channel->anyoneCanAddMembers();
|
||||
const auto invitePhrase = useInviteLinkPhrase
|
||||
? tr::lng_admin_log_admin_invite_link
|
||||
: tr::lng_admin_log_admin_invite_users;
|
||||
const auto callPhrase = channel->isBroadcast()
|
||||
? tr::lng_admin_log_admin_manage_calls_channel
|
||||
: tr::lng_admin_log_admin_manage_calls;
|
||||
static auto phraseMap = std::map<Flags, tr::phrase<>> {
|
||||
{ Flag::ChangeInfo, tr::lng_admin_log_admin_change_info },
|
||||
{ Flag::PostMessages, tr::lng_admin_log_admin_post_messages },
|
||||
@@ -181,6 +185,7 @@ TextWithEntities GenerateAdminChangeText(
|
||||
{ Flag::AddAdmins, tr::lng_admin_log_admin_add_admins },
|
||||
};
|
||||
phraseMap[Flag::InviteUsers] = invitePhrase;
|
||||
phraseMap[Flag::ManageCall] = callPhrase;
|
||||
|
||||
if (!channel->isMegagroup()) {
|
||||
// Don't display "Ban users" changes in channels.
|
||||
@@ -502,6 +507,7 @@ void GenerateItems(
|
||||
const auto id = event.vid().v;
|
||||
const auto from = history->owner().user(event.vuser_id().v);
|
||||
const auto channel = history->peer->asChannel();
|
||||
const auto broadcast = channel->isBroadcast();
|
||||
const auto &action = event.vaction();
|
||||
const auto date = event.vdate().v;
|
||||
const auto addPart = [&](
|
||||
@@ -825,7 +831,6 @@ void GenerateItems(
|
||||
};
|
||||
|
||||
auto createChangeLinkedChat = [&](const MTPDchannelAdminLogEventActionChangeLinkedChat &action) {
|
||||
const auto broadcast = channel->isBroadcast();
|
||||
const auto now = history->owner().channelLoaded(action.vnew_value().v);
|
||||
if (!now) {
|
||||
auto text = (broadcast
|
||||
@@ -901,12 +906,16 @@ void GenerateItems(
|
||||
};
|
||||
|
||||
auto createStartGroupCall = [&](const MTPDchannelAdminLogEventActionStartGroupCall &data) {
|
||||
const auto text = tr::lng_admin_log_started_group_call(tr::now, lt_from, fromLinkText);
|
||||
const auto text = (broadcast
|
||||
? tr::lng_admin_log_started_group_call_channel
|
||||
: tr::lng_admin_log_started_group_call)(tr::now, lt_from, fromLinkText);
|
||||
addSimpleServiceMessage(text);
|
||||
};
|
||||
|
||||
auto createDiscardGroupCall = [&](const MTPDchannelAdminLogEventActionDiscardGroupCall &data) {
|
||||
const auto text = tr::lng_admin_log_discarded_group_call(tr::now, lt_from, fromLinkText);
|
||||
const auto text = (broadcast
|
||||
? tr::lng_admin_log_discarded_group_call_channel
|
||||
: tr::lng_admin_log_discarded_group_call)(tr::now, lt_from, fromLinkText);
|
||||
addSimpleServiceMessage(text);
|
||||
};
|
||||
|
||||
@@ -932,7 +941,9 @@ void GenerateItems(
|
||||
const auto participantPeer = groupCallParticipantPeer(data.vparticipant());
|
||||
const auto participantPeerLink = participantPeer->createOpenLink();
|
||||
const auto participantPeerLinkText = textcmdLink(2, participantPeer->name);
|
||||
auto text = tr::lng_admin_log_muted_participant(
|
||||
auto text = (broadcast
|
||||
? tr::lng_admin_log_muted_participant_channel
|
||||
: tr::lng_admin_log_muted_participant)(
|
||||
tr::now,
|
||||
lt_from,
|
||||
fromLinkText,
|
||||
@@ -945,7 +956,9 @@ void GenerateItems(
|
||||
const auto participantPeer = groupCallParticipantPeer(data.vparticipant());
|
||||
const auto participantPeerLink = participantPeer->createOpenLink();
|
||||
const auto participantPeerLinkText = textcmdLink(2, participantPeer->name);
|
||||
auto text = tr::lng_admin_log_unmuted_participant(
|
||||
auto text = (broadcast
|
||||
? tr::lng_admin_log_unmuted_participant_channel
|
||||
: tr::lng_admin_log_unmuted_participant)(
|
||||
tr::now,
|
||||
lt_from,
|
||||
fromLinkText,
|
||||
@@ -955,9 +968,13 @@ void GenerateItems(
|
||||
};
|
||||
|
||||
auto createToggleGroupCallSetting = [&](const MTPDchannelAdminLogEventActionToggleGroupCallSetting &data) {
|
||||
const auto text = mtpIsTrue(data.vjoin_muted())
|
||||
? tr::lng_admin_log_disallowed_unmute_self(tr::now, lt_from, fromLinkText)
|
||||
: tr::lng_admin_log_allowed_unmute_self(tr::now, lt_from, fromLinkText);
|
||||
const auto text = (mtpIsTrue(data.vjoin_muted())
|
||||
? (broadcast
|
||||
? tr::lng_admin_log_disallowed_unmute_self_channel
|
||||
: tr::lng_admin_log_disallowed_unmute_self)
|
||||
: (broadcast
|
||||
? tr::lng_admin_log_allowed_unmute_self_channel
|
||||
: tr::lng_admin_log_allowed_unmute_self))(tr::now, lt_from, fromLinkText);
|
||||
addSimpleServiceMessage(text);
|
||||
};
|
||||
|
||||
@@ -1026,7 +1043,9 @@ void GenerateItems(
|
||||
return data.vvolume().value_or(10000);
|
||||
});
|
||||
const auto volumeText = QString::number(volume / 100) + '%';
|
||||
auto text = tr::lng_admin_log_participant_volume(
|
||||
auto text = (broadcast
|
||||
? tr::lng_admin_log_participant_volume_channel
|
||||
: tr::lng_admin_log_participant_volume)(
|
||||
tr::now,
|
||||
lt_from,
|
||||
fromLinkText,
|
||||
|
||||
@@ -275,7 +275,7 @@ Widget::Widget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<ChannelData*> channel)
|
||||
: Window::SectionWidget(parent, controller, PaintedBackground::Section)
|
||||
: Window::SectionWidget(parent, controller, rpl::single<PeerData*>(channel))
|
||||
, _scroll(this, st::historyScroll, false)
|
||||
, _fixedBar(this, controller, channel)
|
||||
, _fixedBarShadow(this)
|
||||
@@ -306,11 +306,6 @@ Widget::Widget(
|
||||
updateAdaptiveLayout();
|
||||
}, lifetime());
|
||||
|
||||
controller->repaintBackgroundRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
update();
|
||||
}, lifetime());
|
||||
|
||||
_inner = _scroll->setOwnedWidget(object_ptr<InnerWidget>(this, controller, channel));
|
||||
_inner->showSearchSignal(
|
||||
) | rpl::start_with_next([=] {
|
||||
@@ -475,7 +470,8 @@ void Widget::paintEvent(QPaintEvent *e) {
|
||||
//auto ms = crl::now();
|
||||
//_historyDownShown.step(ms);
|
||||
|
||||
SectionWidget::PaintBackground(controller(), this, e->rect());
|
||||
const auto clip = e->rect();
|
||||
SectionWidget::PaintBackground(controller(), _inner->theme(), this, clip);
|
||||
}
|
||||
|
||||
void Widget::onScroll() {
|
||||
|
||||
@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_scheduled_messages.h"
|
||||
#include "data/data_send_action.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_channel.h"
|
||||
@@ -335,16 +336,27 @@ void History::draftSavedToCloud() {
|
||||
session().local().writeDrafts(this);
|
||||
}
|
||||
|
||||
HistoryItemsList History::validateForwardDraft() {
|
||||
auto result = owner().idsToItems(_forwardDraft);
|
||||
if (result.size() != _forwardDraft.size()) {
|
||||
setForwardDraft(owner().itemsToIds(result));
|
||||
Data::ResolvedForwardDraft History::resolveForwardDraft(
|
||||
const Data::ForwardDraft &draft) const {
|
||||
return Data::ResolvedForwardDraft{
|
||||
.items = owner().idsToItems(draft.ids),
|
||||
.options = draft.options,
|
||||
};
|
||||
}
|
||||
|
||||
Data::ResolvedForwardDraft History::resolveForwardDraft() {
|
||||
auto result = resolveForwardDraft(_forwardDraft);
|
||||
if (result.items.size() != _forwardDraft.ids.size()) {
|
||||
setForwardDraft({
|
||||
.ids = owner().itemsToIds(result.items),
|
||||
.options = result.options,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void History::setForwardDraft(MessageIdsList &&items) {
|
||||
_forwardDraft = std::move(items);
|
||||
void History::setForwardDraft(Data::ForwardDraft &&draft) {
|
||||
_forwardDraft = std::move(draft);
|
||||
}
|
||||
|
||||
HistoryItem *History::createItem(
|
||||
@@ -458,7 +470,7 @@ void History::unpinAllMessages() {
|
||||
Storage::SharedMediaRemoveAll(
|
||||
peer->id,
|
||||
Storage::SharedMediaType::Pinned));
|
||||
peer->setHasPinnedMessages(false);
|
||||
setHasPinnedMessages(false);
|
||||
for (const auto &message : _messages) {
|
||||
if (message->isPinned()) {
|
||||
message->setIsPinned(false);
|
||||
@@ -751,7 +763,7 @@ not_null<HistoryItem*> History::addNewToBack(
|
||||
item->id,
|
||||
{ from, till }));
|
||||
if (sharedMediaTypes.test(Storage::SharedMediaType::Pinned)) {
|
||||
peer->setHasPinnedMessages(true);
|
||||
setHasPinnedMessages(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1023,7 +1035,7 @@ void History::applyServiceChanges(
|
||||
Storage::SharedMediaType::Pinned,
|
||||
{ id },
|
||||
{ id, ServerMaxMsgId }));
|
||||
peer->setHasPinnedMessages(true);
|
||||
setHasPinnedMessages(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1061,6 +1073,8 @@ void History::applyServiceChanges(
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [&](const MTPDmessageActionSetChatTheme &data) {
|
||||
peer->setThemeEmoji(qs(data.vemoticon()));
|
||||
}, [](const auto &) {
|
||||
});
|
||||
}
|
||||
@@ -1086,7 +1100,7 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
|
||||
if (const auto from = item->from() ? item->from()->asUser() : nullptr) {
|
||||
if (from == item->author()) {
|
||||
_sendActionPainter.clear(from);
|
||||
owner().repliesSendActionPaintersClear(this, from);
|
||||
owner().sendActionManager().repliesPaintersClear(this, from);
|
||||
}
|
||||
from->madeAction(item->date());
|
||||
}
|
||||
@@ -1399,7 +1413,7 @@ void History::addToSharedMedia(
|
||||
std::move(medias[i]),
|
||||
{ from, till }));
|
||||
if (type == Storage::SharedMediaType::Pinned) {
|
||||
peer->setHasPinnedMessages(true);
|
||||
setHasPinnedMessages(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3121,6 +3135,15 @@ void History::removeBlock(not_null<HistoryBlock*> block) {
|
||||
}
|
||||
}
|
||||
|
||||
bool History::hasPinnedMessages() const {
|
||||
return _hasPinnedMessages;
|
||||
}
|
||||
|
||||
void History::setHasPinnedMessages(bool has) {
|
||||
_hasPinnedMessages = has;
|
||||
session().changes().historyUpdated(this, UpdateFlag::PinnedMessages);
|
||||
}
|
||||
|
||||
History::~History() = default;
|
||||
|
||||
HistoryBlock::HistoryBlock(not_null<History*> history)
|
||||
|
||||
@@ -33,6 +33,23 @@ struct Draft;
|
||||
class Session;
|
||||
class Folder;
|
||||
class ChatFilter;
|
||||
|
||||
enum class ForwardOptions {
|
||||
PreserveInfo,
|
||||
NoSenderNames,
|
||||
NoNamesAndCaptions,
|
||||
};
|
||||
|
||||
struct ForwardDraft {
|
||||
MessageIdsList ids;
|
||||
ForwardOptions options = ForwardOptions::PreserveInfo;
|
||||
};
|
||||
|
||||
struct ResolvedForwardDraft {
|
||||
HistoryItemsList items;
|
||||
ForwardOptions options = ForwardOptions::PreserveInfo;
|
||||
};
|
||||
|
||||
} // namespace Data
|
||||
|
||||
namespace Dialogs {
|
||||
@@ -354,11 +371,13 @@ public:
|
||||
void applyCloudDraft();
|
||||
void draftSavedToCloud();
|
||||
|
||||
const MessageIdsList &forwardDraft() const {
|
||||
[[nodiscard]] const Data::ForwardDraft &forwardDraft() const {
|
||||
return _forwardDraft;
|
||||
}
|
||||
HistoryItemsList validateForwardDraft();
|
||||
void setForwardDraft(MessageIdsList &&items);
|
||||
[[nodiscard]] Data::ResolvedForwardDraft resolveForwardDraft(
|
||||
const Data::ForwardDraft &draft) const;
|
||||
[[nodiscard]] Data::ResolvedForwardDraft resolveForwardDraft();
|
||||
void setForwardDraft(Data::ForwardDraft &&draft);
|
||||
|
||||
History *migrateSibling() const;
|
||||
[[nodiscard]] bool useTopPromotion() const;
|
||||
@@ -412,6 +431,9 @@ public:
|
||||
void setInboxReadTill(MsgId upTo);
|
||||
std::optional<int> countStillUnreadLocal(MsgId readTillId) const;
|
||||
|
||||
[[nodiscard]] bool hasPinnedMessages() const;
|
||||
void setHasPinnedMessages(bool has);
|
||||
|
||||
// Still public data.
|
||||
std::deque<std::unique_ptr<HistoryBlock>> blocks;
|
||||
|
||||
@@ -581,6 +603,7 @@ private:
|
||||
|
||||
bool _unreadMark = false;
|
||||
bool _fakeUnreadWhileOpened = false;
|
||||
bool _hasPinnedMessages = false;
|
||||
|
||||
// A pointer to the block that is currently being built.
|
||||
// We hold this pointer so we can destroy it while building
|
||||
@@ -594,7 +617,7 @@ private:
|
||||
Data::HistoryDrafts _drafts;
|
||||
TimeId _acceptCloudDraftsAfter = 0;
|
||||
int _savingCloudDraftRequests = 0;
|
||||
MessageIdsList _forwardDraft;
|
||||
Data::ForwardDraft _forwardDraft;
|
||||
|
||||
QString _topPromotedMessage;
|
||||
QString _topPromotedType;
|
||||
|
||||
@@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "history/view/history_view_context_menu.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/toast/toast.h"
|
||||
@@ -162,6 +163,14 @@ HistoryInner::HistoryInner(
|
||||
, _scrollDateHideTimer([this] { scrollDateHideByTimer(); }) {
|
||||
Instance = this;
|
||||
|
||||
Window::ChatThemeValueFromPeer(
|
||||
controller,
|
||||
_peer
|
||||
) | rpl::start_with_next([=](std::shared_ptr<Ui::ChatTheme> &&theme) {
|
||||
_theme = std::move(theme);
|
||||
controller->setChatStyleTheme(_theme);
|
||||
}, lifetime());
|
||||
|
||||
_touchSelectTimer.setSingleShot(true);
|
||||
connect(&_touchSelectTimer, SIGNAL(timeout()), this, SLOT(onTouchSelect()));
|
||||
|
||||
@@ -615,7 +624,8 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
auto item = view->data();
|
||||
|
||||
auto top = mtop + block->y() + view->y();
|
||||
auto context = _controller->bubblesContext({
|
||||
auto context = _controller->preparePaintContext({
|
||||
.theme = _theme.get(),
|
||||
.visibleAreaTop = _visibleAreaTop,
|
||||
.visibleAreaTopGlobal = visibleAreaTopGlobal,
|
||||
.clip = clip,
|
||||
@@ -663,7 +673,8 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
auto item = view->data();
|
||||
auto readTill = (HistoryItem*)nullptr;
|
||||
auto top = htop + block->y() + view->y();
|
||||
auto context = _controller->bubblesContext({
|
||||
auto context = _controller->preparePaintContext({
|
||||
.theme = _theme.get(),
|
||||
.visibleAreaTop = _visibleAreaTop,
|
||||
.visibleAreaTopGlobal = visibleAreaTopGlobal,
|
||||
.visibleAreaWidth = width(),
|
||||
|
||||
@@ -34,6 +34,7 @@ class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace Ui {
|
||||
class ChatTheme;
|
||||
class PopupMenu;
|
||||
enum class ReportReason;
|
||||
class PathShiftGradient;
|
||||
@@ -55,7 +56,10 @@ public:
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> history);
|
||||
|
||||
Main::Session &session() const;
|
||||
[[nodiscard]] Main::Session &session() const;
|
||||
[[nodiscard]] not_null<Ui::ChatTheme*> theme() const {
|
||||
return _theme.get();
|
||||
}
|
||||
|
||||
void messagesReceived(PeerData *peer, const QVector<MTPMessage> &messages);
|
||||
void messagesReceivedDown(PeerData *peer, const QVector<MTPMessage> &messages);
|
||||
@@ -344,6 +348,7 @@ private:
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
const not_null<PeerData*> _peer;
|
||||
const not_null<History*> _history;
|
||||
std::shared_ptr<Ui::ChatTheme> _theme;
|
||||
|
||||
History *_migrated = nullptr;
|
||||
int _contentWidth = 0;
|
||||
|
||||
@@ -364,7 +364,7 @@ void HistoryItem::setIsPinned(bool pinned) {
|
||||
Storage::SharedMediaType::Pinned,
|
||||
id,
|
||||
{ id, id }));
|
||||
history()->peer->setHasPinnedMessages(true);
|
||||
history()->setHasPinnedMessages(true);
|
||||
} else {
|
||||
_flags &= ~MessageFlag::Pinned;
|
||||
history()->session().storage().remove(Storage::SharedMediaRemoveOne(
|
||||
@@ -553,7 +553,7 @@ void HistoryItem::indexAsNewItem() {
|
||||
types,
|
||||
id));
|
||||
if (types.test(Storage::SharedMediaType::Pinned)) {
|
||||
_history->peer->setHasPinnedMessages(true);
|
||||
_history->setHasPinnedMessages(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -936,20 +936,19 @@ QString HistoryItem::notificationText() const {
|
||||
}
|
||||
|
||||
QString HistoryItem::inDialogsText(DrawInDialog way) const {
|
||||
auto getText = [this]() {
|
||||
const auto plainText = [&] {
|
||||
if (_media) {
|
||||
if (_groupId) {
|
||||
return textcmdLink(1, TextUtilities::Clean(tr::lng_in_dlg_album(tr::now)));
|
||||
}
|
||||
return _media->chatListText();
|
||||
return _media->chatListText(way);
|
||||
} else if (!emptyText()) {
|
||||
return TextUtilities::Clean(_text.toString());
|
||||
}
|
||||
return QString();
|
||||
};
|
||||
const auto plainText = getText();
|
||||
}();
|
||||
const auto sender = [&]() -> PeerData* {
|
||||
if (isPost() || isEmpty() || (way == DrawInDialog::WithoutSender)) {
|
||||
if (isPost() || isEmpty() || (way != DrawInDialog::Normal)) {
|
||||
return nullptr;
|
||||
} else if (!_history->peer->isUser() || out()) {
|
||||
return displayFrom();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user