Compare commits
196 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a87529b8c9 | ||
|
|
0c30bbf40a | ||
|
|
f75bfb4369 | ||
|
|
0c34ba638c | ||
|
|
4af9c801ae | ||
|
|
e06bf16980 | ||
|
|
1018745b0b | ||
|
|
47b157bf32 | ||
|
|
09547bd6d5 | ||
|
|
d17f21c10d | ||
|
|
f11339361d | ||
|
|
04b9d4bdb5 | ||
|
|
1d8d2b6251 | ||
|
|
190bf8fc5f | ||
|
|
f463d3ec6d | ||
|
|
f9d10094ac | ||
|
|
9fc87c3cb8 | ||
|
|
0d9c30423f | ||
|
|
c4bc76c022 | ||
|
|
241f5e1d26 | ||
|
|
76f0abecfd | ||
|
|
ca45fb617e | ||
|
|
dfd63e66ff | ||
|
|
d85f162bff | ||
|
|
1d4fbc64e2 | ||
|
|
9fd32fc85b | ||
|
|
f7f195eb98 | ||
|
|
12c7bd8ee1 | ||
|
|
cd5ef069c0 | ||
|
|
f90fdce422 | ||
|
|
423254f7eb | ||
|
|
c9c0d74b68 | ||
|
|
8bf6013342 | ||
|
|
c1ab1acd44 | ||
|
|
90b955534a | ||
|
|
61c7bf2f5e | ||
|
|
ee5423762a | ||
|
|
6d29dc3b36 | ||
|
|
aa10934e85 | ||
|
|
9ff1fbcf47 | ||
|
|
51c1dc20e1 | ||
|
|
3a3bf84cfc | ||
|
|
037b936613 | ||
|
|
b631d09a40 | ||
|
|
fd4f384c3b | ||
|
|
428a501bac | ||
|
|
3ca57ae50d | ||
|
|
f1eddcd584 | ||
|
|
f979df3dfe | ||
|
|
eebcdb842d | ||
|
|
048658f838 | ||
|
|
8fd17e2e8f | ||
|
|
5c13214244 | ||
|
|
12a020cd09 | ||
|
|
b98e02f326 | ||
|
|
9a521c5340 | ||
|
|
5f26e92f5c | ||
|
|
3d78c637a2 | ||
|
|
2ab2eed633 | ||
|
|
bb7018424a | ||
|
|
a770b5d4cd | ||
|
|
c92a9585e1 | ||
|
|
cedb2d31af | ||
|
|
95da2dbc34 | ||
|
|
f9b2a8d6ac | ||
|
|
a773ad7b02 | ||
|
|
32287a51f9 | ||
|
|
37a4c79c81 | ||
|
|
95ee17bd54 | ||
|
|
469c6770fb | ||
|
|
e38123cc48 | ||
|
|
e3d7bf771f | ||
|
|
1cda90c3c5 | ||
|
|
9c86f0e0a5 | ||
|
|
910f16312c | ||
|
|
639b4bdd27 | ||
|
|
4951eeac98 | ||
|
|
79106e0c01 | ||
|
|
7485f0c960 | ||
|
|
03bdd80b2f | ||
|
|
dd74f57a66 | ||
|
|
534772722e | ||
|
|
6343221d7b | ||
|
|
4929de2bfb | ||
|
|
ac8f924909 | ||
|
|
95afcbb485 | ||
|
|
229bc56cc8 | ||
|
|
4b045a602c | ||
|
|
1e3b72ab74 | ||
|
|
bc63d9fe53 | ||
|
|
d7cb8b7065 | ||
|
|
30f4d870c5 | ||
|
|
ad515c6f4a | ||
|
|
056949416d | ||
|
|
ab6d9ff73c | ||
|
|
8b766dda8e | ||
|
|
ee0f66d746 | ||
|
|
7893ad0558 | ||
|
|
b0c5a75fb9 | ||
|
|
1c313da888 | ||
|
|
ee210ea701 | ||
|
|
e0c0d79be9 | ||
|
|
cb3bad31fa | ||
|
|
917696be36 | ||
|
|
253816641c | ||
|
|
ca2692473d | ||
|
|
8000dfac01 | ||
|
|
85cca51154 | ||
|
|
87ea49e094 | ||
|
|
66bf48e21e | ||
|
|
451056d2ab | ||
|
|
791ae64a90 | ||
|
|
3b4563772e | ||
|
|
16b786186b | ||
|
|
7be1c4ca2f | ||
|
|
17fba16c23 | ||
|
|
5fc4dcd172 | ||
|
|
b27a2cd34a | ||
|
|
569340c7d3 | ||
|
|
3715fa4b1e | ||
|
|
d95e54cb1a | ||
|
|
470b67f557 | ||
|
|
c46bcef9ff | ||
|
|
c31cda0587 | ||
|
|
5758f756c9 | ||
|
|
f199205592 | ||
|
|
2b656f7745 | ||
|
|
7be286751b | ||
|
|
04617e4a12 | ||
|
|
405ccb8580 | ||
|
|
131ef4f15a | ||
|
|
07f45b7eab | ||
|
|
87addd41b1 | ||
|
|
0a4f91a53d | ||
|
|
3b76a908a4 | ||
|
|
9e3bc966c8 | ||
|
|
149c69809d | ||
|
|
97a239a8b4 | ||
|
|
bd7cee2252 | ||
|
|
66d0d6e8fe | ||
|
|
117d6192fa | ||
|
|
763bdf8798 | ||
|
|
56a82600f8 | ||
|
|
a3e993253c | ||
|
|
04d5158ae3 | ||
|
|
38e4daacd4 | ||
|
|
44d156760e | ||
|
|
d66541989e | ||
|
|
56c4d164f3 | ||
|
|
e2c1c4c8de | ||
|
|
a465117689 | ||
|
|
c2117e7722 | ||
|
|
7de28fc4bd | ||
|
|
529ef64257 | ||
|
|
9cb5423d40 | ||
|
|
dd136350fb | ||
|
|
ef7087348a | ||
|
|
8eac2dcb78 | ||
|
|
f690f93f32 | ||
|
|
77ebdd3576 | ||
|
|
3e895d0e85 | ||
|
|
385a7eb00d | ||
|
|
1c9775baf9 | ||
|
|
fb96d2eef8 | ||
|
|
debeb61540 | ||
|
|
03cdddfe18 | ||
|
|
caef7dde24 | ||
|
|
0b08810d5a | ||
|
|
694f771131 | ||
|
|
956bb876f6 | ||
|
|
99037d3d46 | ||
|
|
ea0a616453 | ||
|
|
815a18be94 | ||
|
|
3814b0833d | ||
|
|
549789bfb7 | ||
|
|
a539fad3e2 | ||
|
|
ee96d78656 | ||
|
|
288c1130b9 | ||
|
|
d1083a1fb4 | ||
|
|
e1fe373504 | ||
|
|
1a06714f3a | ||
|
|
7316d24ca4 | ||
|
|
b814c6307a | ||
|
|
06fbb2edc4 | ||
|
|
0ee47bb10f | ||
|
|
9f228d8146 | ||
|
|
de270d5283 | ||
|
|
3f041545bf | ||
|
|
1139a59818 | ||
|
|
1e64ba8f29 | ||
|
|
65342559c7 | ||
|
|
e150db1d0b | ||
|
|
4548b14b45 | ||
|
|
dac1128dc9 | ||
|
|
c3944d95b4 | ||
|
|
02b65a42f7 |
@@ -170,6 +170,7 @@ buildXkbCommon() {
|
||||
git clone https://github.com/xkbcommon/libxkbcommon.git
|
||||
|
||||
cd "$EXTERNAL/libxkbcommon"
|
||||
git checkout xkbcommon-0.8.4
|
||||
./autogen.sh --prefix=$XKB_PATH
|
||||
make $MAKE_ARGS
|
||||
sudo make install
|
||||
|
||||
@@ -295,6 +295,40 @@ index c4cb8e65c0..45793e364f 100644
|
||||
channels[i].socket->close();
|
||||
delete channels[i].socket;
|
||||
}
|
||||
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
|
||||
index 94235a48dd..9abd2cc0a1 100644
|
||||
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
|
||||
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
|
||||
@@ -2045,6 +2045,9 @@ void QNetworkReplyHttpImplPrivate::finished()
|
||||
{
|
||||
Q_Q(QNetworkReplyHttpImpl);
|
||||
|
||||
+ // Patch: Fix crash in Linux (by crashreports).
|
||||
+ QPointer<QNetworkReplyHttpImpl> guard = q;
|
||||
+
|
||||
if (state == Finished || state == Aborted || state == WaitingForSession)
|
||||
return;
|
||||
|
||||
@@ -2075,6 +2078,9 @@ void QNetworkReplyHttpImplPrivate::finished()
|
||||
#endif
|
||||
}
|
||||
|
||||
+ // Patch: Fix crash in Linux (by crashreports).
|
||||
+ if (!guard) return;
|
||||
+
|
||||
// if we don't know the total size of or we received everything save the cache
|
||||
if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
|
||||
completeCacheSave();
|
||||
@@ -2084,6 +2090,9 @@ void QNetworkReplyHttpImplPrivate::finished()
|
||||
if (isHttpRedirectResponse() && errorCode == QNetworkReply::NoError)
|
||||
return;
|
||||
|
||||
+ // Patch: Fix crash in Linux (by crashreports).
|
||||
+ if (!guard) return;
|
||||
+
|
||||
state = Finished;
|
||||
q->setFinished(true);
|
||||
|
||||
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
|
||||
index 41834b21ae..8cdf4ab145 100644
|
||||
--- a/src/network/socket/qnativesocketengine_win.cpp
|
||||
|
||||
@@ -51,11 +51,6 @@ attentionButtonFgOver: #d14e4e; // default attention button text with mouse over
|
||||
attentionButtonBgOver: #fcdfde; // default attention button background with mouse over
|
||||
attentionButtonBgRipple: #f4c3c2; // default attention button ripple effect
|
||||
|
||||
outlineButtonBg: windowBg; // default left outlined button background (like shared media links in profiles)
|
||||
outlineButtonBgOver: lightButtonBgOver; // default left outlined button background with mouse over
|
||||
outlineButtonOutlineFg: windowBgActive; // default left outlined button left outline border
|
||||
outlineButtonBgRipple: lightButtonBgRipple; // default left outlined button ripple effect
|
||||
|
||||
menuBg: windowBg; // default popup menu background
|
||||
menuBgOver: windowBgOver; // default popup menu item background with mouse over
|
||||
menuBgRipple: windowBgRipple; // default popup menu item ripple effect
|
||||
|
||||
BIN
Telegram/Resources/icons/send_control_schedule.png
Normal file
|
After Width: | Height: | Size: 743 B |
BIN
Telegram/Resources/icons/send_control_schedule@2x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/send_control_schedule@3x.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
Telegram/Resources/icons/send_control_scheduled.png
Normal file
|
After Width: | Height: | Size: 362 B |
BIN
Telegram/Resources/icons/send_control_scheduled@2x.png
Normal file
|
After Width: | Height: | Size: 652 B |
BIN
Telegram/Resources/icons/send_control_scheduled@3x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/send_control_scheduled_dot.png
Normal file
|
After Width: | Height: | Size: 221 B |
BIN
Telegram/Resources/icons/send_control_scheduled_dot@2x.png
Normal file
|
After Width: | Height: | Size: 406 B |
BIN
Telegram/Resources/icons/send_control_scheduled_dot@3x.png
Normal file
|
After Width: | Height: | Size: 622 B |
BIN
Telegram/Resources/icons/theme_preview.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/theme_preview@2x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram/Resources/icons/theme_preview@3x.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
@@ -76,6 +76,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_month_day_year" = "{month} {day}, {year}";
|
||||
"lng_month_year" = "{month} {year}";
|
||||
|
||||
"lng_calendar_beginning" = "Beginning";
|
||||
|
||||
"lng_box_ok" = "OK";
|
||||
"lng_box_done" = "Done";
|
||||
|
||||
@@ -164,6 +166,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_edit_media_album_error" = "This file cannot be saved as a part of an album.";
|
||||
"lng_edit_media_invalid_file" = "Sorry, no way to use this file.";
|
||||
"lng_edit_caption_attach" = "Sorry, you can't attach a new media while you're editing your message.";
|
||||
|
||||
"lng_intro_about" = "Welcome to the official Telegram Desktop app.\nIt's fast and secure.";
|
||||
"lng_start_msgs" = "START MESSAGING";
|
||||
@@ -297,6 +300,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_notification_reply" = "Reply";
|
||||
"lng_notification_hide_all" = "Hide all";
|
||||
"lng_notification_sample" = "This is a sample notification";
|
||||
"lng_notification_reminder" = "Reminder";
|
||||
|
||||
"lng_settings_section_general" = "General";
|
||||
"lng_settings_change_lang" = "Change language";
|
||||
@@ -336,7 +340,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_bg_use_default" = "Use default color theme";
|
||||
"lng_settings_bg_from_gallery" = "Choose from gallery";
|
||||
"lng_settings_bg_from_file" = "Choose from file";
|
||||
"lng_settings_bg_edit_theme" = "Launch theme editor";
|
||||
"lng_settings_bg_theme_edit" = "Edit theme";
|
||||
"lng_settings_bg_theme_create" = "Create new theme";
|
||||
"lng_settings_bg_cloud_themes" = "Custom themes";
|
||||
"lng_settings_bg_show_all" = "Show all themes";
|
||||
"lng_settings_bg_tile" = "Tile background";
|
||||
"lng_settings_adaptive_wide" = "Adaptive layout for wide screens";
|
||||
|
||||
@@ -385,10 +392,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_stickers_emoji" = "Stickers and emoji";
|
||||
"lng_settings_messages" = "Messages";
|
||||
"lng_settings_themes" = "Themes";
|
||||
"lng_settings_theme_blue" = "Blue";
|
||||
"lng_settings_theme_day" = "Day";
|
||||
"lng_settings_theme_classic" = "Classic";
|
||||
"lng_settings_theme_midnight" = "Midnight";
|
||||
"lng_settings_theme_matrix" = "Matrix";
|
||||
"lng_settings_theme_tinted" = "Tinted";
|
||||
"lng_settings_theme_night" = "Night";
|
||||
"lng_settings_theme_accent_title" = "Choose accent color";
|
||||
"lng_settings_data_storage" = "Data and storage";
|
||||
"lng_settings_information" = "Edit profile";
|
||||
"lng_settings_passcode_title" = "Local passcode";
|
||||
@@ -408,6 +416,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_theme_reverting#other" = "Reverting to the old theme in {count} seconds.";
|
||||
"lng_theme_keep_changes" = "Keep changes";
|
||||
"lng_theme_revert" = "Revert";
|
||||
"lng_theme_no_desktop" = "Sorry, this theme doesn't include a version for Telegram Desktop.";
|
||||
"lng_theme_share" = "Share";
|
||||
"lng_theme_edit" = "Edit";
|
||||
"lng_theme_delete" = "Delete";
|
||||
"lng_theme_delete_sure" = "Are you sure you want to delete this theme?";
|
||||
"lng_background_header" = "Background preview";
|
||||
"lng_background_text1" = "Ah, you kids today with techno music! You should enjoy the classics, like Hasselhoff!";
|
||||
"lng_background_text2" = "I can't even take you seriously right now.";
|
||||
@@ -629,6 +642,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edit_privacy_phone_number_always_title" = "Always share with";
|
||||
"lng_edit_privacy_phone_number_never_title" = "Never share with";
|
||||
|
||||
"lng_edit_privacy_phone_number_find" = "Who can find me by my number";
|
||||
"lng_edit_privacy_phone_number_contacts" = "Users who add your number to their contacts will see it on Telegram only if they are your contacts.";
|
||||
|
||||
"lng_edit_privacy_lastseen_title" = "Last seen privacy";
|
||||
"lng_edit_privacy_lastseen_header" = "Who can see your last seen time";
|
||||
"lng_edit_privacy_lastseen_warning" = "Important: you won't be able to see Last Seen times for people with whom you don't share your Last Seen time. Approximate last seen will be shown instead (recently, within a week, within a month).";
|
||||
@@ -1134,6 +1150,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_media_size_limit" = "Limit by size";
|
||||
"lng_media_size_up_to" = "up to {size}";
|
||||
"lng_media_chat_background" = "Chat background";
|
||||
"lng_media_color_theme" = "Color theme";
|
||||
|
||||
"lng_emoji_category1" = "People";
|
||||
"lng_emoji_category2" = "Nature";
|
||||
@@ -1240,6 +1257,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_send_button" = "Send";
|
||||
"lng_send_silent_message" = "Send without sound";
|
||||
"lng_schedule_message" = "Schedule message";
|
||||
"lng_reminder_message" = "Set a reminder";
|
||||
"lng_schedule_title" = "Send this message on...";
|
||||
"lng_remind_title" = "Remind me on...";
|
||||
"lng_schedule_at" = "at";
|
||||
"lng_message_ph" = "Write a message...";
|
||||
"lng_broadcast_ph" = "Broadcast a message...";
|
||||
"lng_broadcast_silent_ph" = "Silent broadcast...";
|
||||
@@ -1259,6 +1281,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_saved_short" = "Save";
|
||||
"lng_saved_forward_here" = "Forward messages here for quick access";
|
||||
|
||||
"lng_scheduled_messages" = "Scheduled Messages";
|
||||
"lng_reminder_messages" = "Reminders";
|
||||
"lng_scheduled_send_now" = "Send message now?";
|
||||
"lng_scheduled_send_now_many#one" = "Send {count} message now?";
|
||||
"lng_scheduled_send_now_many#other" = "Send {count} messages now?";
|
||||
|
||||
"lng_archived_name" = "Archived chats";
|
||||
"lng_archived_add" = "Archive";
|
||||
"lng_archived_remove" = "Unarchive";
|
||||
@@ -1386,6 +1414,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_reply_msg" = "Reply";
|
||||
"lng_context_edit_msg" = "Edit";
|
||||
"lng_context_forward_msg" = "Forward Message";
|
||||
"lng_context_send_now_msg" = "Send now";
|
||||
"lng_context_delete_msg" = "Delete Message";
|
||||
"lng_context_select_msg" = "Select Message";
|
||||
"lng_context_report_msg" = "Report Message";
|
||||
@@ -1395,6 +1424,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_copy_selected" = "Copy Selected Text";
|
||||
"lng_context_copy_selected_items" = "Copy Selected as Text";
|
||||
"lng_context_forward_selected" = "Forward Selected";
|
||||
"lng_context_send_now_selected" = "Send selected now";
|
||||
"lng_context_delete_selected" = "Delete Selected";
|
||||
"lng_context_clear_selection" = "Clear Selection";
|
||||
"lng_send_image_empty" = "Could not send an empty file: {name}";
|
||||
@@ -1472,6 +1502,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_selected_clear" = "Cancel";
|
||||
"lng_selected_delete" = "Delete";
|
||||
"lng_selected_forward" = "Forward";
|
||||
"lng_selected_send_now" = "Send now";
|
||||
"lng_selected_cancel_sure_this" = "Cancel uploading?";
|
||||
"lng_selected_upload_stop" = "Stop";
|
||||
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
|
||||
@@ -1536,6 +1567,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_theme_preview_generating" = "Generating color theme preview...";
|
||||
"lng_theme_preview_invalid" = "Invalid data in this theme file.";
|
||||
"lng_theme_preview_apply" = "Apply this theme";
|
||||
"lng_theme_preview_users#one" = "{count} person is using this theme";
|
||||
"lng_theme_preview_users#other" = "{count} people are using this theme";
|
||||
|
||||
"lng_new_authorization" = "{name},\nWe detected a login into your account from a new device on {day}, {date} at {time}\n\nDevice: {device}\nLocation: {location}\n\nIf this wasn't you, you can go to Settings — Show all sessions and terminate that session.\n\nIf you think that somebody logged in to your account against your will, you can enable two-step verification in Settings.\n\nSincerely,\nThe Telegram Team";
|
||||
|
||||
@@ -1579,9 +1612,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_theme_editor_save_palette" = "Save palette file";
|
||||
"lng_theme_editor_choose_name" = "Save theme file";
|
||||
"lng_theme_editor_error" = "The editor encountered an error :( See 'log.txt' for details.";
|
||||
"lng_theme_editor_sure_close" = "Are you sure you want to close the editor? Your changes won't be saved.";
|
||||
"lng_theme_editor_need_auth" = "You need to log in to save your theme.";
|
||||
"lng_theme_editor_need_unlock" = "You need to unlock Telegram to save your theme.";
|
||||
"lng_theme_editor_done" = "Theme exported successfully!";
|
||||
"lng_theme_editor_title" = "Edit color palette";
|
||||
"lng_theme_editor_export_button" = "Export theme";
|
||||
"lng_theme_editor_save_button" = "Save theme";
|
||||
|
||||
"lng_theme_editor_create_title" = "Create theme";
|
||||
"lng_theme_editor_attach_title" = "Attach desktop theme";
|
||||
"lng_theme_editor_create" = "Create";
|
||||
"lng_theme_editor_name" = "Theme name";
|
||||
"lng_theme_editor_create_description" = "New theme will be based on your currently selected colors and wallpaper. Alternatively, you can import existing theme or color palette from file.";
|
||||
"lng_theme_editor_attach_description" = "You can create desktop part of your theme based on your currently selected colors and wallpaper. Alternatively, you can import existing theme or color palette from file.";
|
||||
"lng_theme_editor_import_existing" = "Import existing theme";
|
||||
"lng_theme_editor_save_title" = "Save theme";
|
||||
"lng_theme_editor_link_about" = "Your theme will be updated for all users each time you change it. Anyone can install it using this link.\n\nTheme links must be longer than 5 characters and use a-z, 0-9 and underscores.";
|
||||
|
||||
"lng_theme_editor_menu_export" = "Export theme";
|
||||
"lng_theme_editor_menu_import" = "Import theme";
|
||||
"lng_theme_editor_menu_show" = "Show palette file";
|
||||
|
||||
"lng_payments_not_supported" = "Sorry, Telegram Desktop doesn't support payments yet. Please use one of our mobile apps to do this.";
|
||||
"lng_payments_receipt_label" = "Receipt";
|
||||
|
||||
@@ -108,7 +108,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
|
||||
storage.fileWebp#1081464c = storage.FileType;
|
||||
|
||||
userEmpty#200250ba id:int = User;
|
||||
user#2e13f4c3 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
|
||||
user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
|
||||
|
||||
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
|
||||
userProfilePhoto#ecd75d8c photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto;
|
||||
@@ -123,11 +123,11 @@ userStatusLastMonth#77ebc742 = UserStatus;
|
||||
chatEmpty#9ba2d800 id:int = Chat;
|
||||
chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
|
||||
chatForbidden#7328bdb id:int title:string = Chat;
|
||||
channel#4df30834 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 id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = 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 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#1b7c9db3 flags:# can_set_username:flags.7?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
|
||||
channelFull#2d895c74 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_view_stats:flags.12?true can_set_location:flags.16?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: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 pts:int = ChatFull;
|
||||
chatFull#1b7c9db3 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:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
|
||||
channelFull#2d895c74 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_view_stats:flags.12?true can_set_location:flags.16?true has_scheduled:flags.19?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: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 pts:int = ChatFull;
|
||||
|
||||
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
|
||||
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
|
||||
@@ -140,7 +140,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
|
||||
chatPhoto#475cdbd5 photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto;
|
||||
|
||||
messageEmpty#83e5de54 id:int = Message;
|
||||
message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;
|
||||
message#452c0e65 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
|
||||
messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
|
||||
|
||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||
@@ -222,7 +222,7 @@ inputReportReasonOther#e1746d0a text:string = ReportReason;
|
||||
inputReportReasonCopyright#9b89f93a = ReportReason;
|
||||
inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
|
||||
|
||||
userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?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 = UserFull;
|
||||
userFull#edf17c12 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 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 = UserFull;
|
||||
|
||||
contact#f911c994 user_id:int mutual:Bool = Contact;
|
||||
|
||||
@@ -344,6 +344,9 @@ updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBanne
|
||||
updateFolderPeers#19360dc0 folder_peers:Vector<FolderPeer> pts:int pts_count:int = Update;
|
||||
updatePeerSettings#6a7e7366 peer:Peer settings:PeerSettings = Update;
|
||||
updatePeerLocated#b4afcfb0 peers:Vector<PeerLocated> = Update;
|
||||
updateNewScheduledMessage#39a51dfb message:Message = Update;
|
||||
updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector<int> = Update;
|
||||
updateTheme#8216fba3 theme:Theme = Update;
|
||||
|
||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||
|
||||
@@ -440,6 +443,7 @@ inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey;
|
||||
inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey;
|
||||
inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey;
|
||||
inputPrivacyKeyPhoneNumber#352dafa = InputPrivacyKey;
|
||||
inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey;
|
||||
|
||||
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
|
||||
privacyKeyChatInvite#500e6dfa = PrivacyKey;
|
||||
@@ -448,6 +452,7 @@ privacyKeyPhoneP2P#39491cc8 = PrivacyKey;
|
||||
privacyKeyForwards#69ec56a3 = PrivacyKey;
|
||||
privacyKeyProfilePhoto#96151fed = PrivacyKey;
|
||||
privacyKeyPhoneNumber#d19ae46d = PrivacyKey;
|
||||
privacyKeyAddedByPhone#42ffd42b = PrivacyKey;
|
||||
|
||||
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
|
||||
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
|
||||
@@ -491,7 +496,7 @@ messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMess
|
||||
|
||||
webPageEmpty#eb1477e8 id:long = WebPage;
|
||||
webPagePending#c586da1c id:long date:int = WebPage;
|
||||
webPage#5f07b4bc flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page = WebPage;
|
||||
webPage#fa64e172 flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document documents:flags.11?Vector<Document> cached_page:flags.10?Page = WebPage;
|
||||
webPageNotModified#85849473 = WebPage;
|
||||
|
||||
authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;
|
||||
@@ -1062,6 +1067,17 @@ channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation;
|
||||
|
||||
peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated;
|
||||
|
||||
restrictionReason#d072acb4 platform:string reason:string text:string = RestrictionReason;
|
||||
|
||||
inputTheme#3c5693e9 id:long access_hash:long = InputTheme;
|
||||
inputThemeSlug#f5890df1 slug:string = InputTheme;
|
||||
|
||||
themeDocumentNotModified#483d270c = Theme;
|
||||
theme#f7d90ce0 flags:# creator:flags.0?true default:flags.1?true id:long access_hash:long slug:string title:string document:flags.2?Document installs_count:int = Theme;
|
||||
|
||||
account.themesNotModified#f41eb622 = account.Themes;
|
||||
account.themes#7f676421 hash:int themes:Vector<Theme> = account.Themes;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@@ -1143,6 +1159,13 @@ account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSet
|
||||
account.resetWallPapers#bb3b9804 = Bool;
|
||||
account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings;
|
||||
account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool;
|
||||
account.uploadTheme#1c3db333 flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document;
|
||||
account.createTheme#2b7ffd7f slug:string title:string document:InputDocument = Theme;
|
||||
account.updateTheme#3b8ea202 flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument = Theme;
|
||||
account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool;
|
||||
account.installTheme#7ae43737 flags:# dark:flags.0?true format:flags.1?string theme:flags.1?InputTheme = Bool;
|
||||
account.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme;
|
||||
account.getThemes#285946f8 format:string hash:int = account.Themes;
|
||||
|
||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
|
||||
@@ -1177,9 +1200,9 @@ messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?t
|
||||
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
|
||||
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
||||
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
|
||||
messages.sendMessage#fa88427a 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> = Updates;
|
||||
messages.sendMedia#b8d1262b 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> = Updates;
|
||||
messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
|
||||
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 grouped:flags.9?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#bd82b658 peer:InputPeer id:Vector<int> reason:ReportReason = Bool;
|
||||
@@ -1223,9 +1246,9 @@ messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
|
||||
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
|
||||
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
|
||||
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
|
||||
messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates;
|
||||
messages.sendInlineBotResult#220815b0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int = Updates;
|
||||
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
|
||||
messages.editMessage#d116f31e flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
|
||||
messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
|
||||
messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer;
|
||||
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
|
||||
@@ -1259,7 +1282,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
|
||||
messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
|
||||
messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = messages.Messages;
|
||||
messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> = Updates;
|
||||
messages.sendMultiMedia#cc0110cb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int = Updates;
|
||||
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
|
||||
messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets;
|
||||
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
|
||||
@@ -1281,6 +1304,10 @@ messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector<MessagesFilter
|
||||
messages.requestUrlAuth#e33f5613 peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
|
||||
messages.acceptUrlAuth#f729ea98 flags:# write_allowed:flags.0?true peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
|
||||
messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool;
|
||||
messages.getScheduledHistory#e2c2685b peer:InputPeer hash:int = messages.Messages;
|
||||
messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector<int> = messages.Messages;
|
||||
messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector<int> = Updates;
|
||||
messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector<int> = Updates;
|
||||
|
||||
updates.getState#edd4882a = updates.State;
|
||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||
@@ -1388,4 +1415,4 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua
|
||||
folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
|
||||
folders.deleteFolder#1c295881 folder_id:int = Updates;
|
||||
|
||||
// LAYER 104
|
||||
// LAYER 105
|
||||
|
||||
@@ -99,6 +99,7 @@ tlsBlockRandom length:int = TlsBlock;
|
||||
tlsBlockZero length:int = TlsBlock;
|
||||
tlsBlockDomain = TlsBlock;
|
||||
tlsBlockGrease seed:int = TlsBlock;
|
||||
tlsBlockPublicKey = TlsBlock;
|
||||
tlsBlockScope entries:Vector<TlsBlock> = TlsBlock;
|
||||
|
||||
---functions---
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="1.8.0.0" />
|
||||
Version="1.8.10.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName>
|
||||
|
||||
@@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,8,0,0
|
||||
PRODUCTVERSION 1,8,0,0
|
||||
FILEVERSION 1,8,10,0
|
||||
PRODUCTVERSION 1,8,10,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -52,10 +52,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "1.8.0.0"
|
||||
VALUE "FileVersion", "1.8.10.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2019"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.8.0.0"
|
||||
VALUE "ProductVersion", "1.8.10.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,8,0,0
|
||||
PRODUCTVERSION 1,8,0,0
|
||||
FILEVERSION 1,8,10,0
|
||||
PRODUCTVERSION 1,8,10,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -43,10 +43,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "1.8.0.0"
|
||||
VALUE "FileVersion", "1.8.10.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2019"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.8.0.0"
|
||||
VALUE "ProductVersion", "1.8.10.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
44
Telegram/SourceFiles/api/api_common.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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 Api {
|
||||
|
||||
struct SendOptions {
|
||||
TimeId scheduled = 0;
|
||||
bool silent = false;
|
||||
bool handleSupportSwitch = false;
|
||||
bool removeWebPageId = false;
|
||||
};
|
||||
|
||||
enum class SendType {
|
||||
Normal,
|
||||
Scheduled,
|
||||
};
|
||||
|
||||
struct SendAction {
|
||||
explicit SendAction(not_null<History*> history) : history(history) {
|
||||
}
|
||||
|
||||
not_null<History*> history;
|
||||
SendOptions options;
|
||||
MsgId replyTo = 0;
|
||||
bool clearDraft = true;
|
||||
bool generateLocal = true;
|
||||
};
|
||||
|
||||
struct MessageToSend {
|
||||
explicit MessageToSend(not_null<History*> history) : action(history) {
|
||||
}
|
||||
|
||||
SendAction action;
|
||||
TextWithTags textWithTags;
|
||||
WebPageId webPageId = 0;
|
||||
};
|
||||
|
||||
} // namespace Api
|
||||
42
Telegram/SourceFiles/api/api_hash.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
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 Api {
|
||||
|
||||
[[nodiscard]] inline uint32 HashInit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void HashUpdate(uint32 &already, uint32 value) {
|
||||
already = (already * 20261) + uint32(value);
|
||||
}
|
||||
|
||||
inline void HashUpdate(uint32 &already, int32 value) {
|
||||
HashUpdate(already, uint32(value));
|
||||
}
|
||||
|
||||
inline void HashUpdate(uint32 &already, uint64 value) {
|
||||
HashUpdate(already, uint32(value >> 32));
|
||||
HashUpdate(already, uint32(value & 0xFFFFFFFFULL));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int32 HashFinalize(uint32 already) {
|
||||
return int32(already & 0x7FFFFFFF);
|
||||
}
|
||||
|
||||
template <typename IntRange>
|
||||
[[nodiscard]] inline int32 CountHash(IntRange &&range) {
|
||||
auto result = HashInit();
|
||||
for (const auto value : range) {
|
||||
HashUpdate(result, value);
|
||||
}
|
||||
return HashFinalize(result);
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_file_origin.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h" // NewMessageFlags.
|
||||
#include "chat_helpers/message_field.h" // ConvertTextTagsToEntities.
|
||||
#include "ui/text/text_entity.h" // TextWithEntities.
|
||||
#include "main/main_session.h"
|
||||
#include "mainwidget.h"
|
||||
@@ -26,36 +27,33 @@ namespace {
|
||||
|
||||
template <typename MediaData>
|
||||
void SendExistingMedia(
|
||||
not_null<History*> history,
|
||||
Api::MessageToSend &&message,
|
||||
not_null<MediaData*> media,
|
||||
const MTPInputMedia &inputMedia,
|
||||
Data::FileOrigin origin,
|
||||
TextWithEntities caption,
|
||||
MsgId replyToId,
|
||||
bool silent) {
|
||||
Fn<MTPInputMedia()> inputMedia,
|
||||
Data::FileOrigin origin) {
|
||||
const auto history = message.action.history;
|
||||
const auto peer = history->peer;
|
||||
const auto session = &history->session();
|
||||
const auto api = &session->api();
|
||||
|
||||
auto options = ApiWrap::SendOptions(history);
|
||||
options.clearDraft = false;
|
||||
options.replyTo = replyToId;
|
||||
options.generateLocal = true;
|
||||
options.silent = silent;
|
||||
message.action.clearDraft = false;
|
||||
message.action.generateLocal = true;
|
||||
api->sendAction(message.action);
|
||||
|
||||
api->sendAction(options);
|
||||
|
||||
const auto newId = FullMsgId(peerToChannel(peer->id), clientMsgId());
|
||||
const auto newId = FullMsgId(
|
||||
peerToChannel(peer->id),
|
||||
session->data().nextLocalMessageId());
|
||||
const auto randomId = rand_value<uint64>();
|
||||
|
||||
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||
if (options.replyTo) {
|
||||
if (message.action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto silentPost = options.silent
|
||||
const auto silentPost = message.action.options.silent
|
||||
|| (channelPost && session->data().notifySilentPosts(peer));
|
||||
if (channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
@@ -74,6 +72,10 @@ void SendExistingMedia(
|
||||
? App::peerName(session->user())
|
||||
: QString();
|
||||
|
||||
auto caption = TextWithEntities{
|
||||
message.textWithTags.text,
|
||||
ConvertTextTagsToEntities(message.textWithTags.tags)
|
||||
};
|
||||
TextUtilities::Trim(caption);
|
||||
auto sentEntities = TextUtilities::EntitiesToMTP(
|
||||
caption.entities,
|
||||
@@ -81,17 +83,25 @@ void SendExistingMedia(
|
||||
if (!sentEntities.v.isEmpty()) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
|
||||
}
|
||||
const auto replyTo = options.replyTo;
|
||||
const auto replyTo = message.action.replyTo;
|
||||
const auto captionText = caption.text;
|
||||
|
||||
if (message.action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
}
|
||||
|
||||
session->data().registerMessageRandomId(randomId, newId);
|
||||
|
||||
history->addNewLocalMessage(
|
||||
newId.msg,
|
||||
flags,
|
||||
clientFlags,
|
||||
0,
|
||||
replyTo,
|
||||
base::unixtime::now(),
|
||||
HistoryItem::NewMessageDate(message.action.options.scheduled),
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
media,
|
||||
@@ -105,11 +115,12 @@ void SendExistingMedia(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
MTP_int(replyTo),
|
||||
inputMedia,
|
||||
inputMedia(),
|
||||
MTP_string(captionText),
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
sentEntities
|
||||
sentEntities,
|
||||
MTP_int(message.action.options.scheduled)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result, randomId);
|
||||
}).fail([=](const RPCError &error) {
|
||||
@@ -124,46 +135,36 @@ void SendExistingMedia(
|
||||
if (media->fileReference() != usedFileReference) {
|
||||
performRequest();
|
||||
} else {
|
||||
api->sendMessageFail(error, peer, newId);
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
api->sendMessageFail(error, peer, newId);
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
}
|
||||
};
|
||||
performRequest();
|
||||
|
||||
if (const auto main = App::main()) {
|
||||
main->finishForwarding(history, options.silent);
|
||||
main->finishForwarding(message.action);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SendExistingDocument(
|
||||
not_null<History*> history,
|
||||
not_null<DocumentData*> document,
|
||||
bool silent) {
|
||||
SendExistingDocument(history, document, {}, 0, silent);
|
||||
}
|
||||
|
||||
void SendExistingDocument(
|
||||
not_null<History*> history,
|
||||
not_null<DocumentData*> document,
|
||||
TextWithEntities caption,
|
||||
MsgId replyToId,
|
||||
bool silent) {
|
||||
SendExistingMedia(
|
||||
history,
|
||||
document,
|
||||
MTP_inputMediaDocument(
|
||||
Api::MessageToSend &&message,
|
||||
not_null<DocumentData*> document) {
|
||||
const auto inputMedia = [=] {
|
||||
return MTP_inputMediaDocument(
|
||||
MTP_flags(0),
|
||||
document->mtpInput(),
|
||||
MTPint()),
|
||||
document->stickerOrGifOrigin(),
|
||||
caption,
|
||||
replyToId,
|
||||
silent);
|
||||
MTPint());
|
||||
};
|
||||
SendExistingMedia(
|
||||
std::move(message),
|
||||
document,
|
||||
inputMedia,
|
||||
document->stickerOrGifOrigin());
|
||||
|
||||
if (document->sticker()) {
|
||||
if (const auto main = App::main()) {
|
||||
@@ -174,29 +175,19 @@ void SendExistingDocument(
|
||||
}
|
||||
|
||||
void SendExistingPhoto(
|
||||
not_null<History*> history,
|
||||
not_null<PhotoData*> photo,
|
||||
bool silent) {
|
||||
SendExistingPhoto(history, photo, {}, 0, silent);
|
||||
}
|
||||
|
||||
void SendExistingPhoto(
|
||||
not_null<History*> history,
|
||||
not_null<PhotoData*> photo,
|
||||
TextWithEntities caption,
|
||||
MsgId replyToId,
|
||||
bool silent) {
|
||||
SendExistingMedia(
|
||||
history,
|
||||
photo,
|
||||
MTP_inputMediaPhoto(
|
||||
Api::MessageToSend &&message,
|
||||
not_null<PhotoData*> photo) {
|
||||
const auto inputMedia = [=] {
|
||||
return MTP_inputMediaPhoto(
|
||||
MTP_flags(0),
|
||||
photo->mtpInput(),
|
||||
MTPint()),
|
||||
Data::FileOrigin(),
|
||||
caption,
|
||||
replyToId,
|
||||
silent);
|
||||
MTPint());
|
||||
};
|
||||
SendExistingMedia(
|
||||
std::move(message),
|
||||
photo,
|
||||
inputMedia,
|
||||
Data::FileOrigin());
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -9,32 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class History;
|
||||
class DocumentData;
|
||||
struct TextWithEntities;
|
||||
|
||||
namespace Api {
|
||||
|
||||
void SendExistingDocument(
|
||||
not_null<History*> history,
|
||||
not_null<DocumentData*> document,
|
||||
bool silent = false);
|
||||
struct MessageToSend;
|
||||
|
||||
void SendExistingDocument(
|
||||
not_null<History*> history,
|
||||
not_null<DocumentData*> document,
|
||||
TextWithEntities caption,
|
||||
MsgId replyToId = 0,
|
||||
bool silent = false);
|
||||
Api::MessageToSend &&message,
|
||||
not_null<DocumentData*> document);
|
||||
|
||||
void SendExistingPhoto(
|
||||
not_null<History*> history,
|
||||
not_null<PhotoData*> photo,
|
||||
bool silent = false);
|
||||
|
||||
void SendExistingPhoto(
|
||||
not_null<History*> history,
|
||||
not_null<PhotoData*> photo,
|
||||
TextWithEntities caption,
|
||||
MsgId replyToId = 0,
|
||||
bool silent = false);
|
||||
Api::MessageToSend &&message,
|
||||
not_null<PhotoData*> photo);
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -15,11 +15,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_sparse_ids.h"
|
||||
#include "data/data_search_controller.h"
|
||||
#include "data/data_scheduled_messages.h"
|
||||
#include "data/data_channel_admins.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "dialogs/dialogs_key.h"
|
||||
#include "core/core_cloud_password.h"
|
||||
#include "core/application.h"
|
||||
@@ -156,19 +158,13 @@ MTPVector<MTPDocumentAttribute> ComposeSendingDocumentAttributes(
|
||||
|
||||
} // namespace
|
||||
|
||||
ApiWrap::SendOptions::SendOptions(not_null<History*> history)
|
||||
: history(history) {
|
||||
}
|
||||
|
||||
ApiWrap::MessageToSend::MessageToSend(not_null<History*> history)
|
||||
: history(history) {
|
||||
}
|
||||
|
||||
MTPInputPrivacyKey ApiWrap::Privacy::Input(Key key) {
|
||||
switch (key) {
|
||||
case Privacy::Key::Calls: return MTP_inputPrivacyKeyPhoneCall();
|
||||
case Privacy::Key::Invites: return MTP_inputPrivacyKeyChatInvite();
|
||||
case Privacy::Key::PhoneNumber: return MTP_inputPrivacyKeyPhoneNumber();
|
||||
case Privacy::Key::AddedByPhone:
|
||||
return MTP_inputPrivacyKeyAddedByPhone();
|
||||
case Privacy::Key::LastSeen:
|
||||
return MTP_inputPrivacyKeyStatusTimestamp();
|
||||
case Privacy::Key::CallsPeer2Peer:
|
||||
@@ -187,6 +183,8 @@ std::optional<ApiWrap::Privacy::Key> ApiWrap::Privacy::KeyFromMTP(
|
||||
switch (type) {
|
||||
case mtpc_privacyKeyPhoneNumber:
|
||||
case mtpc_inputPrivacyKeyPhoneNumber: return Key::PhoneNumber;
|
||||
case mtpc_privacyKeyAddedByPhone:
|
||||
case mtpc_inputPrivacyKeyAddedByPhone: return Key::AddedByPhone;
|
||||
case mtpc_privacyKeyStatusTimestamp:
|
||||
case mtpc_inputPrivacyKeyStatusTimestamp: return Key::LastSeen;
|
||||
case mtpc_privacyKeyChatInvite:
|
||||
@@ -544,6 +542,7 @@ void ApiWrap::toggleHistoryArchived(
|
||||
void ApiWrap::sendMessageFail(
|
||||
const RPCError &error,
|
||||
not_null<PeerData*> peer,
|
||||
uint64 randomId,
|
||||
FullMsgId itemId) {
|
||||
if (error.type() == qstr("PEER_FLOOD")) {
|
||||
Ui::show(Box<InformBox>(
|
||||
@@ -561,11 +560,17 @@ void ApiWrap::sendMessageFail(
|
||||
const auto left = error.type().mid(chop).toInt();
|
||||
if (const auto channel = peer->asChannel()) {
|
||||
const auto seconds = channel->slowmodeSeconds();
|
||||
channel->growSlowmodeLastMessage(
|
||||
base::unixtime::now() - (left - seconds));
|
||||
if (seconds >= left) {
|
||||
channel->growSlowmodeLastMessage(
|
||||
base::unixtime::now() - (left - seconds));
|
||||
} else {
|
||||
requestFullPeer(peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (const auto item = _session->data().message(itemId)) {
|
||||
Assert(randomId != 0);
|
||||
session().data().unregisterMessageRandomId(randomId);
|
||||
item->sendFailed();
|
||||
}
|
||||
}
|
||||
@@ -2996,7 +3001,13 @@ void ApiWrap::refreshFileReference(
|
||||
};
|
||||
origin.data.match([&](Data::FileOriginMessage data) {
|
||||
if (const auto item = _session->data().message(data)) {
|
||||
if (const auto channel = item->history()->peer->asChannel()) {
|
||||
if (item->isScheduled()) {
|
||||
const auto &scheduled = session().data().scheduledMessages();
|
||||
const auto realId = scheduled.lookupId(item);
|
||||
request(MTPmessages_GetScheduledMessages(
|
||||
item->history()->peer->input,
|
||||
MTP_vector<MTPint>(1, MTP_int(realId))));
|
||||
} else if (const auto channel = item->history()->peer->asChannel()) {
|
||||
request(MTPchannels_GetMessages(
|
||||
channel->inputChannel,
|
||||
MTP_vector<MTPInputMessage>(
|
||||
@@ -3053,6 +3064,13 @@ void ApiWrap::refreshFileReference(
|
||||
MTP_inputWallPaper(
|
||||
MTP_long(data.paperId),
|
||||
MTP_long(data.accessHash))));
|
||||
}, [&](Data::FileOriginTheme data) {
|
||||
request(MTPaccount_GetTheme(
|
||||
MTP_string(Data::CloudThemes::Format()),
|
||||
MTP_inputTheme(
|
||||
MTP_long(data.themeId),
|
||||
MTP_long(data.accessHash)),
|
||||
MTP_long(0)));
|
||||
}, [&](std::nullopt_t) {
|
||||
fail();
|
||||
});
|
||||
@@ -3103,6 +3121,7 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs
|
||||
for (const auto &[position, index] : indices) {
|
||||
const auto item = _session->data().addNewMessage(
|
||||
v->at(index),
|
||||
MTPDmessage_ClientFlags(),
|
||||
NewMessageType::Existing);
|
||||
if (item) {
|
||||
_session->data().requestItemResize(item);
|
||||
@@ -3548,7 +3567,8 @@ void ApiWrap::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||
switch (updates.type()) {
|
||||
case mtpc_updateShortMessage: {
|
||||
const auto &d = updates.c_updateShortMessage();
|
||||
const auto flags = mtpCastFlags(d.vflags().v) | MTPDmessage::Flag::f_from_id;
|
||||
const auto flags = mtpCastFlags(d.vflags().v)
|
||||
| MTPDmessage::Flag::f_from_id;
|
||||
const auto peerUserId = d.is_out()
|
||||
? d.vuser_id()
|
||||
: MTP_int(_session->userId());
|
||||
@@ -3570,7 +3590,10 @@ void ApiWrap::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||
MTPint(),
|
||||
MTPint(),
|
||||
MTPstring(),
|
||||
MTPlong()),
|
||||
MTPlong(),
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>()),
|
||||
MTPDmessage_ClientFlags(),
|
||||
NewMessageType::Unread);
|
||||
} break;
|
||||
|
||||
@@ -3595,7 +3618,10 @@ void ApiWrap::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||
MTPint(),
|
||||
MTPint(),
|
||||
MTPstring(),
|
||||
MTPlong()),
|
||||
MTPlong(),
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>()),
|
||||
MTPDmessage_ClientFlags(),
|
||||
NewMessageType::Unread);
|
||||
} break;
|
||||
|
||||
@@ -3620,7 +3646,10 @@ void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||
}
|
||||
}
|
||||
if (needToAdd) {
|
||||
_session->data().addNewMessage(d.vmessage(), NewMessageType::Unread);
|
||||
_session->data().addNewMessage(
|
||||
d.vmessage(),
|
||||
MTPDmessage_ClientFlags(),
|
||||
NewMessageType::Unread);
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -3709,7 +3738,10 @@ void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||
}
|
||||
}
|
||||
if (needToAdd) {
|
||||
_session->data().addNewMessage(d.vmessage(), NewMessageType::Unread);
|
||||
_session->data().addNewMessage(
|
||||
d.vmessage(),
|
||||
MTPDmessage_ClientFlags(),
|
||||
NewMessageType::Unread);
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -4368,15 +4400,15 @@ void ApiWrap::userPhotosDone(
|
||||
// )).send();
|
||||
//}
|
||||
|
||||
void ApiWrap::sendAction(const SendOptions &options) {
|
||||
readServerHistory(options.history);
|
||||
options.history->getReadyFor(ShowAtTheEndMsgId);
|
||||
_sendActions.fire_copy(options);
|
||||
void ApiWrap::sendAction(const SendAction &action) {
|
||||
readServerHistory(action.history);
|
||||
action.history->getReadyFor(ShowAtTheEndMsgId);
|
||||
_sendActions.fire_copy(action);
|
||||
}
|
||||
|
||||
void ApiWrap::forwardMessages(
|
||||
HistoryItemsList &&items,
|
||||
const SendOptions &options,
|
||||
const SendAction &action,
|
||||
FnMut<void()> &&successCallback) {
|
||||
Expects(!items.empty());
|
||||
|
||||
@@ -4392,17 +4424,18 @@ void ApiWrap::forwardMessages(
|
||||
}
|
||||
|
||||
const auto count = int(items.size());
|
||||
const auto genClientSideMessage = options.generateLocal && (count < 2);
|
||||
const auto history = options.history;
|
||||
const auto genClientSideMessage = action.generateLocal && (count < 2);
|
||||
const auto history = action.history;
|
||||
const auto peer = history->peer;
|
||||
|
||||
readServerHistory(history);
|
||||
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto silentPost = options.silent
|
||||
const auto silentPost = action.options.silent
|
||||
|| (channelPost && _session->data().notifySilentPosts(peer));
|
||||
|
||||
auto flags = MTPDmessage::Flags(0);
|
||||
auto clientFlags = MTPDmessage_ClientFlags();
|
||||
auto sendFlags = MTPmessages_ForwardMessages::Flags(0);
|
||||
if (channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
@@ -4416,12 +4449,18 @@ void ApiWrap::forwardMessages(
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_ForwardMessages::Flag::f_silent;
|
||||
}
|
||||
if (action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
sendFlags |= MTPmessages_ForwardMessages::Flag::f_schedule_date;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
}
|
||||
|
||||
auto forwardFrom = items.front()->history()->peer;
|
||||
auto currentGroupId = items.front()->groupId();
|
||||
auto ids = QVector<MTPint>();
|
||||
auto randomIds = QVector<MTPlong>();
|
||||
auto localIds = std::unique_ptr<std::vector<FullMsgId>>();
|
||||
auto localIds = std::unique_ptr<base::flat_map<uint64, FullMsgId>>();
|
||||
|
||||
const auto sendAccumulated = [&] {
|
||||
if (shared) {
|
||||
@@ -4436,7 +4475,8 @@ void ApiWrap::forwardMessages(
|
||||
forwardFrom->input,
|
||||
MTP_vector<MTPint>(ids),
|
||||
MTP_vector<MTPlong>(randomIds),
|
||||
peer->input
|
||||
peer->input,
|
||||
MTP_int(action.options.scheduled)
|
||||
)).done([=, callback = std::move(successCallback)](
|
||||
const MTPUpdates &updates) {
|
||||
applyUpdates(updates);
|
||||
@@ -4445,8 +4485,8 @@ void ApiWrap::forwardMessages(
|
||||
}
|
||||
}).fail([=, ids = std::move(localIds)](const RPCError &error) {
|
||||
if (ids) {
|
||||
for (const auto &itemId : *ids) {
|
||||
sendMessageFail(error, peer, itemId);
|
||||
for (const auto &[randomId, itemId] : *ids) {
|
||||
sendMessageFail(error, peer, randomId, itemId);
|
||||
}
|
||||
} else {
|
||||
sendMessageFail(error, peer);
|
||||
@@ -4463,12 +4503,12 @@ void ApiWrap::forwardMessages(
|
||||
ids.reserve(count);
|
||||
randomIds.reserve(count);
|
||||
for (const auto item : items) {
|
||||
auto randomId = rand_value<uint64>();
|
||||
const auto randomId = rand_value<uint64>();
|
||||
if (genClientSideMessage) {
|
||||
if (const auto message = item->toHistoryMessage()) {
|
||||
const auto newId = FullMsgId(
|
||||
peerToChannel(peer->id),
|
||||
clientMsgId());
|
||||
session().data().nextLocalMessageId());
|
||||
const auto self = _session->user();
|
||||
const auto messageFromId = channelPost
|
||||
? UserId(0)
|
||||
@@ -4479,15 +4519,16 @@ void ApiWrap::forwardMessages(
|
||||
history->addNewLocalMessage(
|
||||
newId.msg,
|
||||
flags,
|
||||
base::unixtime::now(),
|
||||
clientFlags,
|
||||
HistoryItem::NewMessageDate(action.options.scheduled),
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
message);
|
||||
_session->data().registerMessageRandomId(randomId, newId);
|
||||
if (!localIds) {
|
||||
localIds = std::make_unique<std::vector<FullMsgId>>();
|
||||
localIds = std::make_unique<base::flat_map<uint64, FullMsgId>>();
|
||||
}
|
||||
localIds->push_back(newId);
|
||||
localIds->emplace(randomId, newId);
|
||||
}
|
||||
}
|
||||
const auto newFrom = item->history()->peer;
|
||||
@@ -4509,14 +4550,14 @@ void ApiWrap::shareContact(
|
||||
const QString &phone,
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
const SendOptions &options) {
|
||||
const SendAction &action) {
|
||||
const auto userId = UserId(0);
|
||||
sendSharedContact(phone, firstName, lastName, userId, options);
|
||||
sendSharedContact(phone, firstName, lastName, userId, action);
|
||||
}
|
||||
|
||||
void ApiWrap::shareContact(
|
||||
not_null<UserData*> user,
|
||||
const SendOptions &options) {
|
||||
const SendAction &action) {
|
||||
const auto userId = peerToUser(user->id);
|
||||
const auto phone = _session->data().findContactPhone(user);
|
||||
if (phone.isEmpty()) {
|
||||
@@ -4527,7 +4568,7 @@ void ApiWrap::shareContact(
|
||||
user->firstName,
|
||||
user->lastName,
|
||||
userId,
|
||||
options);
|
||||
action);
|
||||
}
|
||||
|
||||
void ApiWrap::sendSharedContact(
|
||||
@@ -4535,17 +4576,20 @@ void ApiWrap::sendSharedContact(
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
UserId userId,
|
||||
const SendOptions &options) {
|
||||
sendAction(options);
|
||||
const SendAction &action) {
|
||||
sendAction(action);
|
||||
|
||||
const auto history = options.history;
|
||||
const auto history = action.history;
|
||||
const auto peer = history->peer;
|
||||
|
||||
const auto newId = FullMsgId(history->channelId(), clientMsgId());
|
||||
const auto newId = FullMsgId(
|
||||
history->channelId(),
|
||||
session().data().nextLocalMessageId());
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
|
||||
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
|
||||
if (options.replyTo) {
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
if (action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
if (channelPost) {
|
||||
@@ -4557,6 +4601,11 @@ void ApiWrap::sendSharedContact(
|
||||
} else {
|
||||
flags |= MTPDmessage::Flag::f_from_id;
|
||||
}
|
||||
if (action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
}
|
||||
const auto messageFromId = channelPost ? 0 : _session->userId();
|
||||
const auto messagePostAuthor = channelPost
|
||||
? App::peerName(_session->user())
|
||||
@@ -4571,8 +4620,8 @@ void ApiWrap::sendSharedContact(
|
||||
peerToMTP(peer->id),
|
||||
MTPMessageFwdHeader(),
|
||||
MTPint(),
|
||||
MTP_int(options.replyTo),
|
||||
MTP_int(base::unixtime::now()),
|
||||
MTP_int(action.replyTo),
|
||||
MTP_int(HistoryItem::NewMessageDate(action.options.scheduled)),
|
||||
MTP_string(),
|
||||
MTP_messageMediaContact(
|
||||
MTP_string(phone),
|
||||
@@ -4585,7 +4634,10 @@ void ApiWrap::sendSharedContact(
|
||||
MTP_int(views),
|
||||
MTPint(),
|
||||
MTP_string(messagePostAuthor),
|
||||
MTPlong()),
|
||||
MTPlong(),
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>()),
|
||||
clientFlags,
|
||||
NewMessageType::Unread);
|
||||
|
||||
const auto media = MTP_inputMediaContact(
|
||||
@@ -4593,7 +4645,9 @@ void ApiWrap::sendSharedContact(
|
||||
MTP_string(firstName),
|
||||
MTP_string(lastName),
|
||||
MTP_string(vcard));
|
||||
sendMedia(item, media, _session->data().notifySilentPosts(peer));
|
||||
auto options = action.options;
|
||||
options.silent = _session->data().notifySilentPosts(peer);
|
||||
sendMedia(item, media, options);
|
||||
|
||||
if (const auto main = App::main()) {
|
||||
_session->data().sendHistoryChangeNotifications();
|
||||
@@ -4606,9 +4660,9 @@ void ApiWrap::sendVoiceMessage(
|
||||
QByteArray result,
|
||||
VoiceWaveform waveform,
|
||||
int duration,
|
||||
const SendOptions &options) {
|
||||
const SendAction &action) {
|
||||
const auto caption = TextWithTags();
|
||||
const auto to = fileLoadTaskOptions(options);
|
||||
const auto to = fileLoadTaskOptions(action);
|
||||
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
||||
result,
|
||||
duration,
|
||||
@@ -4621,12 +4675,12 @@ void ApiWrap::editMedia(
|
||||
Storage::PreparedList &&list,
|
||||
SendMediaType type,
|
||||
TextWithTags &&caption,
|
||||
const SendOptions &options,
|
||||
const SendAction &action,
|
||||
MsgId msgIdToEdit) {
|
||||
if (list.files.empty()) return;
|
||||
|
||||
auto &file = list.files.front();
|
||||
const auto to = fileLoadTaskOptions(options);
|
||||
const auto to = fileLoadTaskOptions(action);
|
||||
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
||||
file.path,
|
||||
file.content,
|
||||
@@ -4643,22 +4697,22 @@ void ApiWrap::sendFiles(
|
||||
SendMediaType type,
|
||||
TextWithTags &&caption,
|
||||
std::shared_ptr<SendingAlbum> album,
|
||||
const SendOptions &options) {
|
||||
const SendAction &action) {
|
||||
const auto haveCaption = !caption.text.isEmpty();
|
||||
const auto isAlbum = (album != nullptr);
|
||||
const auto compressImages = (type == SendMediaType::Photo);
|
||||
if (haveCaption && !list.canAddCaption(isAlbum, compressImages)) {
|
||||
auto message = MessageToSend(options.history);
|
||||
auto message = MessageToSend(action.history);
|
||||
message.textWithTags = std::move(caption);
|
||||
message.replyTo = options.replyTo;
|
||||
message.clearDraft = false;
|
||||
message.action = action;
|
||||
message.action.clearDraft = false;
|
||||
sendMessage(std::move(message));
|
||||
caption = TextWithTags();
|
||||
}
|
||||
|
||||
const auto to = fileLoadTaskOptions(options);
|
||||
const auto to = fileLoadTaskOptions(action);
|
||||
if (album) {
|
||||
album->silent = to.silent;
|
||||
album->options = to.options;
|
||||
}
|
||||
auto tasks = std::vector<std::unique_ptr<Task>>();
|
||||
tasks.reserve(list.files.size());
|
||||
@@ -4697,8 +4751,8 @@ void ApiWrap::sendFiles(
|
||||
void ApiWrap::sendFile(
|
||||
const QByteArray &fileContent,
|
||||
SendMediaType type,
|
||||
const SendOptions &options) {
|
||||
const auto to = fileLoadTaskOptions(options);
|
||||
const SendAction &action) {
|
||||
const auto to = fileLoadTaskOptions(action);
|
||||
auto caption = TextWithTags();
|
||||
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
||||
QString(),
|
||||
@@ -4712,7 +4766,7 @@ void ApiWrap::sendFile(
|
||||
void ApiWrap::sendUploadedPhoto(
|
||||
FullMsgId localId,
|
||||
const MTPInputFile &file,
|
||||
bool silent) {
|
||||
Api::SendOptions options) {
|
||||
if (const auto item = _session->data().message(localId)) {
|
||||
const auto media = MTP_inputMediaUploadedPhoto(
|
||||
MTP_flags(0),
|
||||
@@ -4722,7 +4776,7 @@ void ApiWrap::sendUploadedPhoto(
|
||||
if (const auto groupId = item->groupId()) {
|
||||
uploadAlbumMedia(item, groupId, media);
|
||||
} else {
|
||||
sendMedia(item, media, silent);
|
||||
sendMedia(item, media, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4731,7 +4785,7 @@ void ApiWrap::sendUploadedDocument(
|
||||
FullMsgId localId,
|
||||
const MTPInputFile &file,
|
||||
const std::optional<MTPInputFile> &thumb,
|
||||
bool silent) {
|
||||
Api::SendOptions options) {
|
||||
if (const auto item = _session->data().message(localId)) {
|
||||
auto media = item->media();
|
||||
if (auto document = media ? media->document() : nullptr) {
|
||||
@@ -4754,7 +4808,7 @@ void ApiWrap::sendUploadedDocument(
|
||||
if (groupId) {
|
||||
uploadAlbumMedia(item, groupId, media);
|
||||
} else {
|
||||
sendMedia(item, media, silent);
|
||||
sendMedia(item, media, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4764,7 +4818,7 @@ void ApiWrap::editUploadedFile(
|
||||
FullMsgId localId,
|
||||
const MTPInputFile &file,
|
||||
const std::optional<MTPInputFile> &thumb,
|
||||
bool silent,
|
||||
Api::SendOptions options,
|
||||
bool isDocument) {
|
||||
const auto item = _session->data().message(localId);
|
||||
if (!item) {
|
||||
@@ -4829,7 +4883,8 @@ void ApiWrap::editUploadedFile(
|
||||
MTP_string(item->originalText().text),
|
||||
*media,
|
||||
MTPReplyMarkup(),
|
||||
sentEntities
|
||||
sentEntities,
|
||||
MTP_int(0) // schedule_date
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
item->clearSavedMedia();
|
||||
item->setIsLocalUpdateMedia(true);
|
||||
@@ -4861,18 +4916,13 @@ void ApiWrap::cancelLocalItem(not_null<HistoryItem*> item) {
|
||||
}
|
||||
|
||||
void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
const auto history = message.history;
|
||||
const auto history = message.action.history;
|
||||
const auto peer = history->peer;
|
||||
auto &textWithTags = message.textWithTags;
|
||||
|
||||
auto options = ApiWrap::SendOptions(history);
|
||||
options.clearDraft = message.clearDraft;
|
||||
options.replyTo = message.replyTo;
|
||||
options.silent = message.silent;
|
||||
options.generateLocal = true;
|
||||
options.webPageId = message.webPageId;
|
||||
options.handleSupportSwitch = message.handleSupportSwitch;
|
||||
sendAction(options);
|
||||
auto action = message.action;
|
||||
action.generateLocal = true;
|
||||
sendAction(action);
|
||||
|
||||
if (!peer->canWrite()) {
|
||||
return;
|
||||
@@ -4892,7 +4942,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
HistoryItem *lastMessage = nullptr;
|
||||
|
||||
while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
|
||||
auto newId = FullMsgId(peerToChannel(peer->id), clientMsgId());
|
||||
auto newId = FullMsgId(
|
||||
peerToChannel(peer->id),
|
||||
session().data().nextLocalMessageId());
|
||||
auto randomId = rand_value<uint64>();
|
||||
|
||||
TextUtilities::Trim(sending);
|
||||
@@ -4902,8 +4954,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
|
||||
MTPstring msgText(MTP_string(sending.text));
|
||||
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_entities;
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto sendFlags = MTPmessages_SendMessage::Flags(0);
|
||||
if (message.replyTo) {
|
||||
if (action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
@@ -4919,7 +4972,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
flags |= MTPDmessage::Flag::f_media;
|
||||
}
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto silentPost = message.silent
|
||||
const auto silentPost = action.options.silent
|
||||
|| (channelPost && _session->data().notifySilentPosts(peer));
|
||||
if (channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
@@ -4938,7 +4991,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
if (!sentEntities.v.isEmpty()) {
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_entities;
|
||||
}
|
||||
if (message.clearDraft) {
|
||||
if (action.clearDraft) {
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_clear_draft;
|
||||
history->clearCloudDraft();
|
||||
history->setSentDraftText(QString());
|
||||
@@ -4947,6 +5000,12 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
auto messagePostAuthor = channelPost
|
||||
? App::peerName(_session->user())
|
||||
: QString();
|
||||
if (action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
}
|
||||
lastMessage = history->addNewMessage(
|
||||
MTP_message(
|
||||
MTP_flags(flags),
|
||||
@@ -4955,8 +5014,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
peerToMTP(peer->id),
|
||||
MTPMessageFwdHeader(),
|
||||
MTPint(),
|
||||
MTP_int(message.replyTo),
|
||||
MTP_int(base::unixtime::now()),
|
||||
MTP_int(action.replyTo),
|
||||
MTP_int(
|
||||
HistoryItem::NewMessageDate(action.options.scheduled)),
|
||||
msgText,
|
||||
media,
|
||||
MTPReplyMarkup(),
|
||||
@@ -4964,16 +5024,20 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
MTP_int(1),
|
||||
MTPint(),
|
||||
MTP_string(messagePostAuthor),
|
||||
MTPlong()),
|
||||
MTPlong(),
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>()),
|
||||
clientFlags,
|
||||
NewMessageType::Unread);
|
||||
history->sendRequestId = request(MTPmessages_SendMessage(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
MTP_int(message.replyTo),
|
||||
MTP_int(action.replyTo),
|
||||
msgText,
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
sentEntities
|
||||
sentEntities,
|
||||
MTP_int(action.options.scheduled)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
applyUpdates(result, randomId);
|
||||
history->clearSentDraftText(QString());
|
||||
@@ -4981,7 +5045,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
if (error.type() == qstr("MESSAGE_EMPTY")) {
|
||||
lastMessage->destroy();
|
||||
} else {
|
||||
sendMessageFail(error, peer, newId);
|
||||
sendMessageFail(error, peer, randomId, newId);
|
||||
}
|
||||
history->clearSentDraftText(QString());
|
||||
}).afterRequest(history->sendRequestId
|
||||
@@ -4989,7 +5053,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
}
|
||||
|
||||
if (const auto main = App::main()) {
|
||||
main->finishForwarding(history, message.silent);
|
||||
main->finishForwarding(action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5028,22 +5092,25 @@ void ApiWrap::sendBotStart(not_null<UserData*> bot, PeerData *chat) {
|
||||
void ApiWrap::sendInlineResult(
|
||||
not_null<UserData*> bot,
|
||||
not_null<InlineBots::Result*> data,
|
||||
const SendOptions &options) {
|
||||
sendAction(options);
|
||||
const SendAction &action) {
|
||||
sendAction(action);
|
||||
|
||||
const auto history = options.history;
|
||||
const auto history = action.history;
|
||||
const auto peer = history->peer;
|
||||
const auto newId = FullMsgId(peerToChannel(peer->id), clientMsgId());
|
||||
const auto newId = FullMsgId(
|
||||
peerToChannel(peer->id),
|
||||
session().data().nextLocalMessageId());
|
||||
const auto randomId = rand_value<uint64>();
|
||||
|
||||
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto sendFlags = MTPmessages_SendInlineBotResult::Flag::f_clear_draft | 0;
|
||||
if (options.replyTo) {
|
||||
if (action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
|
||||
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
bool channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
bool silentPost = options.silent
|
||||
bool silentPost = action.options.silent
|
||||
|| (channelPost && _session->data().notifySilentPosts(peer));
|
||||
if (channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
@@ -5060,25 +5127,29 @@ void ApiWrap::sendInlineResult(
|
||||
if (bot) {
|
||||
flags |= MTPDmessage::Flag::f_via_bot_id;
|
||||
}
|
||||
if (action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_schedule_date;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
}
|
||||
|
||||
auto messageFromId = channelPost ? 0 : _session->userId();
|
||||
auto messagePostAuthor = channelPost
|
||||
const auto messageFromId = channelPost ? 0 : _session->userId();
|
||||
const auto messagePostAuthor = channelPost
|
||||
? App::peerName(_session->user())
|
||||
: QString();
|
||||
MTPint messageDate = MTP_int(base::unixtime::now());
|
||||
UserId messageViaBotId = bot ? peerToUser(bot->id) : 0;
|
||||
MsgId messageId = newId.msg;
|
||||
|
||||
_session->data().registerMessageRandomId(randomId, newId);
|
||||
|
||||
data->addToHistory(
|
||||
history,
|
||||
flags,
|
||||
messageId,
|
||||
clientFlags,
|
||||
newId.msg,
|
||||
messageFromId,
|
||||
messageDate,
|
||||
messageViaBotId,
|
||||
options.replyTo,
|
||||
MTP_int(HistoryItem::NewMessageDate(action.options.scheduled)),
|
||||
bot ? peerToUser(bot->id) : 0,
|
||||
action.replyTo,
|
||||
messagePostAuthor);
|
||||
|
||||
history->clearCloudDraft();
|
||||
@@ -5087,21 +5158,22 @@ void ApiWrap::sendInlineResult(
|
||||
history->sendRequestId = request(MTPmessages_SendInlineBotResult(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
MTP_int(options.replyTo),
|
||||
MTP_int(action.replyTo),
|
||||
MTP_long(randomId),
|
||||
MTP_long(data->getQueryId()),
|
||||
MTP_string(data->getId())
|
||||
MTP_string(data->getId()),
|
||||
MTP_int(action.options.scheduled)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
applyUpdates(result, randomId);
|
||||
history->clearSentDraftText(QString());
|
||||
}).fail([=](const RPCError &error) {
|
||||
sendMessageFail(error, peer, newId);
|
||||
sendMessageFail(error, peer, randomId, newId);
|
||||
history->clearSentDraftText(QString());
|
||||
}).afterRequest(history->sendRequestId
|
||||
).send();
|
||||
|
||||
if (const auto main = App::main()) {
|
||||
main->finishForwarding(history, options.silent);
|
||||
main->finishForwarding(action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5183,17 +5255,17 @@ void ApiWrap::uploadAlbumMedia(
|
||||
void ApiWrap::sendMedia(
|
||||
not_null<HistoryItem*> item,
|
||||
const MTPInputMedia &media,
|
||||
bool silent) {
|
||||
Api::SendOptions options) {
|
||||
const auto randomId = rand_value<uint64>();
|
||||
_session->data().registerMessageRandomId(randomId, item->fullId());
|
||||
|
||||
sendMediaWithRandomId(item, media, silent, randomId);
|
||||
sendMediaWithRandomId(item, media, options, randomId);
|
||||
}
|
||||
|
||||
void ApiWrap::sendMediaWithRandomId(
|
||||
not_null<HistoryItem*> item,
|
||||
const MTPInputMedia &media,
|
||||
bool silent,
|
||||
Api::SendOptions options,
|
||||
uint64 randomId) {
|
||||
const auto history = item->history();
|
||||
const auto replyTo = item->replyToId();
|
||||
@@ -5208,11 +5280,14 @@ void ApiWrap::sendMediaWithRandomId(
|
||||
| (replyTo
|
||||
? MTPmessages_SendMedia::Flag::f_reply_to_msg_id
|
||||
: MTPmessages_SendMedia::Flag(0))
|
||||
| (silent
|
||||
| (options.silent
|
||||
? MTPmessages_SendMedia::Flag::f_silent
|
||||
: MTPmessages_SendMedia::Flag(0))
|
||||
| (!sentEntities.v.isEmpty()
|
||||
? MTPmessages_SendMedia::Flag::f_entities
|
||||
: MTPmessages_SendMedia::Flag(0))
|
||||
| (options.scheduled
|
||||
? MTPmessages_SendMedia::Flag::f_schedule_date
|
||||
: MTPmessages_SendMedia::Flag(0));
|
||||
|
||||
const auto peer = history->peer;
|
||||
@@ -5225,11 +5300,12 @@ void ApiWrap::sendMediaWithRandomId(
|
||||
MTP_string(caption.text),
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
sentEntities
|
||||
sentEntities,
|
||||
MTP_int(options.scheduled)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
applyUpdates(result);
|
||||
}).fail([=](const RPCError &error) {
|
||||
sendMessageFail(error, peer, itemId);
|
||||
sendMessageFail(error, peer, randomId, itemId);
|
||||
}).afterRequest(
|
||||
history->sendRequestId
|
||||
).send();
|
||||
@@ -5293,7 +5369,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
||||
sendMediaWithRandomId(
|
||||
sample,
|
||||
single.vmedia(),
|
||||
album->silent,
|
||||
album->options,
|
||||
single.vrandom_id().v);
|
||||
_sendingAlbums.remove(groupId);
|
||||
return;
|
||||
@@ -5304,22 +5380,26 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
||||
| (replyTo
|
||||
? MTPmessages_SendMultiMedia::Flag::f_reply_to_msg_id
|
||||
: MTPmessages_SendMultiMedia::Flag(0))
|
||||
| (album->silent
|
||||
| (album->options.silent
|
||||
? MTPmessages_SendMultiMedia::Flag::f_silent
|
||||
: MTPmessages_SendMultiMedia::Flag(0))
|
||||
| (album->options.scheduled
|
||||
? MTPmessages_SendMultiMedia::Flag::f_schedule_date
|
||||
: MTPmessages_SendMultiMedia::Flag(0));
|
||||
const auto peer = history->peer;
|
||||
history->sendRequestId = request(MTPmessages_SendMultiMedia(
|
||||
MTP_flags(flags),
|
||||
peer->input,
|
||||
MTP_int(replyTo),
|
||||
MTP_vector<MTPInputSingleMedia>(medias)
|
||||
MTP_vector<MTPInputSingleMedia>(medias),
|
||||
MTP_int(album->options.scheduled)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_sendingAlbums.remove(groupId);
|
||||
applyUpdates(result);
|
||||
}).fail([=](const RPCError &error) {
|
||||
if (const auto album = _sendingAlbums.take(groupId)) {
|
||||
for (const auto &item : (*album)->items) {
|
||||
sendMessageFail(error, peer, item.msgId);
|
||||
sendMessageFail(error, peer, item.randomId, item.msgId);
|
||||
}
|
||||
} else {
|
||||
sendMessageFail(error, peer);
|
||||
@@ -5329,36 +5409,22 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
||||
).send();
|
||||
}
|
||||
|
||||
FileLoadTo ApiWrap::fileLoadTaskOptions(const SendOptions &options) const {
|
||||
const auto peer = options.history->peer;
|
||||
return FileLoadTo(
|
||||
peer->id,
|
||||
options.silent || _session->data().notifySilentPosts(peer),
|
||||
options.replyTo);
|
||||
}
|
||||
|
||||
void ApiWrap::requestSupportContact(FnMut<void(const MTPUser &)> callback) {
|
||||
_supportContactCallbacks.push_back(std::move(callback));
|
||||
if (_supportContactCallbacks.size() > 1) {
|
||||
return;
|
||||
FileLoadTo ApiWrap::fileLoadTaskOptions(const SendAction &action) const {
|
||||
const auto peer = action.history->peer;
|
||||
auto options = action.options;
|
||||
if (_session->data().notifySilentPosts(peer)) {
|
||||
options.silent = true;
|
||||
}
|
||||
request(MTPhelp_GetSupport(
|
||||
)).done([=](const MTPhelp_Support &result) {
|
||||
result.match([&](const MTPDhelp_support &data) {
|
||||
for (auto &handler : base::take(_supportContactCallbacks)) {
|
||||
handler(data.vuser());
|
||||
}
|
||||
});
|
||||
}).fail([=](const RPCError &error) {
|
||||
_supportContactCallbacks.clear();
|
||||
}).send();
|
||||
return FileLoadTo(peer->id, action.options, action.replyTo);
|
||||
}
|
||||
|
||||
void ApiWrap::uploadPeerPhoto(not_null<PeerData*> peer, QImage &&image) {
|
||||
peer = peer->migrateToOrMe();
|
||||
const auto ready = PreparePeerPhoto(peer->id, std::move(image));
|
||||
|
||||
const auto fakeId = FullMsgId(peerToChannel(peer->id), clientMsgId());
|
||||
const auto fakeId = FullMsgId(
|
||||
peerToChannel(peer->id),
|
||||
session().data().nextLocalMessageId());
|
||||
const auto already = ranges::find(
|
||||
_peerPhotoUploads,
|
||||
peer,
|
||||
@@ -5766,30 +5832,33 @@ void ApiWrap::setSelfDestructDays(int days) {
|
||||
|
||||
void ApiWrap::createPoll(
|
||||
const PollData &data,
|
||||
const SendOptions &options,
|
||||
const SendAction &action,
|
||||
FnMut<void()> done,
|
||||
FnMut<void(const RPCError &error)> fail) {
|
||||
sendAction(options);
|
||||
sendAction(action);
|
||||
|
||||
const auto history = options.history;
|
||||
const auto history = action.history;
|
||||
const auto peer = history->peer;
|
||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||
if (options.replyTo) {
|
||||
if (action.replyTo) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
if (options.clearDraft) {
|
||||
if (action.clearDraft) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_clear_draft;
|
||||
history->clearLocalDraft();
|
||||
history->clearCloudDraft();
|
||||
}
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto silentPost = options.silent
|
||||
const auto silentPost = action.options.silent
|
||||
|| (channelPost && _session->data().notifySilentPosts(peer));
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
if (action.options.scheduled) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||
}
|
||||
|
||||
const auto replyTo = options.replyTo;
|
||||
const auto replyTo = action.replyTo;
|
||||
history->sendRequestId = request(MTPmessages_SendMedia(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
@@ -5798,7 +5867,8 @@ void ApiWrap::createPoll(
|
||||
MTP_string(),
|
||||
MTP_long(rand_value<uint64>()),
|
||||
MTPReplyMarkup(),
|
||||
MTPVector<MTPMessageEntity>()
|
||||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(action.options.scheduled)
|
||||
)).done([=, done = std::move(done)](const MTPUpdates &result) mutable {
|
||||
applyUpdates(result);
|
||||
done();
|
||||
@@ -5874,7 +5944,8 @@ void ApiWrap::closePoll(not_null<HistoryItem*> item) {
|
||||
MTPstring(),
|
||||
MTP_inputMediaPoll(PollDataToMTP(poll)),
|
||||
MTPReplyMarkup(),
|
||||
MTPVector<MTPMessageEntity>()
|
||||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(0) // schedule_date
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_pollCloseRequestIds.erase(itemId);
|
||||
applyUpdates(result);
|
||||
|
||||
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/event_stream.h>
|
||||
#include "api/api_common.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/flat_map.h"
|
||||
#include "base/flat_set.h"
|
||||
@@ -65,15 +65,6 @@ inline QString ToString(uint64 value) {
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename IntRange>
|
||||
inline int32 CountHash(IntRange &&range) {
|
||||
uint32 acc = 0;
|
||||
for (auto value : range) {
|
||||
acc += (acc * 20261) + uint32(value);
|
||||
}
|
||||
return int32(acc & 0x7FFFFFFF);
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...Types,
|
||||
typename = std::enable_if_t<(sizeof...(Types) > 0)>>
|
||||
@@ -96,9 +87,13 @@ QString RequestKey(Types &&...values) {
|
||||
|
||||
class ApiWrap : public MTP::Sender, private base::Subscriber {
|
||||
public:
|
||||
using SendAction = Api::SendAction;
|
||||
using MessageToSend = Api::MessageToSend;
|
||||
|
||||
struct Privacy {
|
||||
enum class Key {
|
||||
PhoneNumber,
|
||||
AddedByPhone,
|
||||
LastSeen,
|
||||
Calls,
|
||||
Invites,
|
||||
@@ -375,31 +370,21 @@ public:
|
||||
not_null<PeerData*> peer,
|
||||
const std::vector<not_null<UserData*>> &users);
|
||||
|
||||
struct SendOptions {
|
||||
SendOptions(not_null<History*> history);
|
||||
|
||||
not_null<History*> history;
|
||||
MsgId replyTo = 0;
|
||||
WebPageId webPageId = 0;
|
||||
bool silent = false;
|
||||
bool clearDraft = false;
|
||||
bool generateLocal = true;
|
||||
bool handleSupportSwitch = false;
|
||||
};
|
||||
rpl::producer<SendOptions> sendActions() const {
|
||||
rpl::producer<SendAction> sendActions() const {
|
||||
return _sendActions.events();
|
||||
}
|
||||
void sendAction(const SendOptions &options);
|
||||
void sendAction(const SendAction &action);
|
||||
void forwardMessages(
|
||||
HistoryItemsList &&items,
|
||||
const SendOptions &options,
|
||||
const SendAction &action,
|
||||
FnMut<void()> &&successCallback = nullptr);
|
||||
void shareContact(
|
||||
const QString &phone,
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
const SendOptions &options);
|
||||
void shareContact(not_null<UserData*> user, const SendOptions &options);
|
||||
const SendAction &action);
|
||||
void shareContact(not_null<UserData*> user, const SendAction &action);
|
||||
void readServerHistory(not_null<History*> history);
|
||||
void readServerHistoryForce(not_null<History*> history);
|
||||
//void readFeed( // #feed
|
||||
@@ -410,67 +395,55 @@ public:
|
||||
QByteArray result,
|
||||
VoiceWaveform waveform,
|
||||
int duration,
|
||||
const SendOptions &options);
|
||||
const SendAction &action);
|
||||
void sendFiles(
|
||||
Storage::PreparedList &&list,
|
||||
SendMediaType type,
|
||||
TextWithTags &&caption,
|
||||
std::shared_ptr<SendingAlbum> album,
|
||||
const SendOptions &options);
|
||||
const SendAction &action);
|
||||
void sendFile(
|
||||
const QByteArray &fileContent,
|
||||
SendMediaType type,
|
||||
const SendOptions &options);
|
||||
const SendAction &action);
|
||||
|
||||
void editMedia(
|
||||
Storage::PreparedList &&list,
|
||||
SendMediaType type,
|
||||
TextWithTags &&caption,
|
||||
const SendOptions &options,
|
||||
const SendAction &action,
|
||||
MsgId msgIdToEdit);
|
||||
|
||||
void sendUploadedPhoto(
|
||||
FullMsgId localId,
|
||||
const MTPInputFile &file,
|
||||
bool silent);
|
||||
Api::SendOptions options);
|
||||
void sendUploadedDocument(
|
||||
FullMsgId localId,
|
||||
const MTPInputFile &file,
|
||||
const std::optional<MTPInputFile> &thumb,
|
||||
bool silent);
|
||||
Api::SendOptions options);
|
||||
void editUploadedFile(
|
||||
FullMsgId localId,
|
||||
const MTPInputFile &file,
|
||||
const std::optional<MTPInputFile> &thumb,
|
||||
bool silent,
|
||||
Api::SendOptions options,
|
||||
bool isDocument);
|
||||
|
||||
void cancelLocalItem(not_null<HistoryItem*> item);
|
||||
|
||||
struct MessageToSend {
|
||||
MessageToSend(not_null<History*> history);
|
||||
|
||||
not_null<History*> history;
|
||||
TextWithTags textWithTags;
|
||||
MsgId replyTo = 0;
|
||||
WebPageId webPageId = 0;
|
||||
bool silent = false;
|
||||
bool clearDraft = true;
|
||||
bool handleSupportSwitch = false;
|
||||
};
|
||||
void sendMessage(MessageToSend &&message);
|
||||
void sendBotStart(not_null<UserData*> bot, PeerData *chat = nullptr);
|
||||
void sendInlineResult(
|
||||
not_null<UserData*> bot,
|
||||
not_null<InlineBots::Result*> data,
|
||||
const SendOptions &options);
|
||||
const SendAction &action);
|
||||
void sendMessageFail(
|
||||
const RPCError &error,
|
||||
not_null<PeerData*> peer,
|
||||
uint64 randomId = 0,
|
||||
FullMsgId itemId = FullMsgId());
|
||||
|
||||
void requestSupportContact(FnMut<void(const MTPUser&)> callback);
|
||||
|
||||
void uploadPeerPhoto(not_null<PeerData*> peer, QImage &&image);
|
||||
void clearPeerPhoto(not_null<PhotoData*> photo);
|
||||
|
||||
@@ -498,7 +471,7 @@ public:
|
||||
|
||||
void createPoll(
|
||||
const PollData &data,
|
||||
const SendOptions &options,
|
||||
const SendAction &action,
|
||||
FnMut<void()> done,
|
||||
FnMut<void(const RPCError &error)> fail);
|
||||
void sendPollVotes(
|
||||
@@ -643,7 +616,7 @@ private:
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
UserId userId,
|
||||
const SendOptions &options);
|
||||
const SendAction &action);
|
||||
|
||||
void deleteHistory(
|
||||
not_null<PeerData*> peer,
|
||||
@@ -677,13 +650,13 @@ private:
|
||||
void sendMedia(
|
||||
not_null<HistoryItem*> item,
|
||||
const MTPInputMedia &media,
|
||||
bool silent);
|
||||
Api::SendOptions options);
|
||||
void sendMediaWithRandomId(
|
||||
not_null<HistoryItem*> item,
|
||||
const MTPInputMedia &media,
|
||||
bool silent,
|
||||
Api::SendOptions options,
|
||||
uint64 randomId);
|
||||
FileLoadTo fileLoadTaskOptions(const SendOptions &options) const;
|
||||
FileLoadTo fileLoadTaskOptions(const SendAction &action) const;
|
||||
|
||||
//void readFeeds(); // #feed
|
||||
|
||||
@@ -826,7 +799,7 @@ private:
|
||||
not_null<Data::Folder*>,
|
||||
DialogsLoadState> _foldersLoadState;
|
||||
|
||||
rpl::event_stream<SendOptions> _sendActions;
|
||||
rpl::event_stream<SendAction> _sendActions;
|
||||
|
||||
struct ReadRequest {
|
||||
ReadRequest(mtpRequestId requestId, MsgId upTo)
|
||||
|
||||
@@ -47,6 +47,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtGui/QFontDatabase>
|
||||
|
||||
#ifdef OS_MAC_OLD
|
||||
#include <libexif/exif-data.h>
|
||||
#endif // OS_MAC_OLD
|
||||
|
||||
@@ -57,19 +57,38 @@ private:
|
||||
|
||||
class BigNum {
|
||||
public:
|
||||
BigNum() : _data(BN_new()) {
|
||||
BigNum() = default;
|
||||
BigNum(const BigNum &other)
|
||||
: _data((other.failed() || other.isZero())
|
||||
? nullptr
|
||||
: BN_dup(other.raw()))
|
||||
, _failed(other._failed) {
|
||||
}
|
||||
BigNum(const BigNum &other) : BigNum() {
|
||||
*this = other;
|
||||
BigNum(BigNum &&other)
|
||||
: _data(std::exchange(other._data, nullptr))
|
||||
, _failed(std::exchange(other._failed, false)) {
|
||||
}
|
||||
BigNum &operator=(const BigNum &other) {
|
||||
if (other.failed() || !BN_copy(raw(), other.raw())) {
|
||||
if (other.failed()) {
|
||||
_failed = true;
|
||||
} else if (other.isZero()) {
|
||||
clear();
|
||||
_failed = false;
|
||||
} else if (!_data) {
|
||||
_data = BN_dup(other.raw());
|
||||
_failed = false;
|
||||
} else {
|
||||
_failed = !BN_copy(raw(), other.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &operator=(BigNum &&other) {
|
||||
std::swap(_data, other._data);
|
||||
std::swap(_failed, other._failed);
|
||||
return *this;
|
||||
}
|
||||
~BigNum() {
|
||||
BN_clear_free(raw());
|
||||
clear();
|
||||
}
|
||||
|
||||
explicit BigNum(unsigned int word) : BigNum() {
|
||||
@@ -79,64 +98,74 @@ public:
|
||||
setBytes(bytes);
|
||||
}
|
||||
|
||||
void setWord(unsigned int word) {
|
||||
if (!BN_set_word(raw(), word)) {
|
||||
_failed = true;
|
||||
BigNum &setWord(unsigned int word) {
|
||||
if (!word) {
|
||||
clear();
|
||||
_failed = false;
|
||||
} else {
|
||||
_failed = !BN_set_word(raw(), word);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void setBytes(bytes::const_span bytes) {
|
||||
if (!BN_bin2bn(
|
||||
BigNum &setBytes(bytes::const_span bytes) {
|
||||
if (bytes.empty()) {
|
||||
clear();
|
||||
_failed = false;
|
||||
} else {
|
||||
_failed = !BN_bin2bn(
|
||||
reinterpret_cast<const unsigned char*>(bytes.data()),
|
||||
bytes.size(),
|
||||
raw())) {
|
||||
_failed = true;
|
||||
raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void setAdd(const BigNum &a, const BigNum &b) {
|
||||
BigNum &setAdd(const BigNum &a, const BigNum &b) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else if (!BN_add(raw(), a.raw(), b.raw())) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = !BN_add(raw(), a.raw(), b.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void setSub(const BigNum &a, const BigNum &b) {
|
||||
BigNum &setSub(const BigNum &a, const BigNum &b) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else if (!BN_sub(raw(), a.raw(), b.raw())) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = !BN_sub(raw(), a.raw(), b.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void setSubWord(unsigned int word) {
|
||||
if (failed()) {
|
||||
return;
|
||||
} else if (!BN_sub_word(raw(), word)) {
|
||||
_failed = true;
|
||||
}
|
||||
}
|
||||
void setMul(
|
||||
BigNum &setMul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mul(raw(), a.raw(), b.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = !BN_mul(raw(), a.raw(), b.raw(), context.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BN_ULONG setDivWord(BN_ULONG word) {
|
||||
Expects(word != 0);
|
||||
if (failed()) {
|
||||
return (BN_ULONG)-1;
|
||||
}
|
||||
|
||||
auto result = BN_div_word(raw(), word);
|
||||
if (result == (BN_ULONG)-1) {
|
||||
BigNum &setModAdd(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_add(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return result;
|
||||
return *this;
|
||||
}
|
||||
void setModSub(
|
||||
BigNum &setModSub(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &m,
|
||||
@@ -149,9 +178,12 @@ public:
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void setModMul(
|
||||
BigNum &setModMul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &m,
|
||||
@@ -164,9 +196,29 @@ public:
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void setModExp(
|
||||
BigNum &setModInverse(
|
||||
const BigNum &a,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_inverse(raw(), a.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setModExp(
|
||||
const BigNum &base,
|
||||
const BigNum &power,
|
||||
const BigNum &m,
|
||||
@@ -179,23 +231,34 @@ public:
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isNegative() const {
|
||||
return failed() ? false : BN_is_negative(raw());
|
||||
[[nodiscard]] bool isZero() const {
|
||||
return !failed() && (!_data || BN_is_zero(raw()));
|
||||
}
|
||||
|
||||
bool isPrime(const Context &context = Context()) const {
|
||||
if (failed()) {
|
||||
[[nodiscard]] bool isOne() const {
|
||||
return !failed() && _data && BN_is_one(raw());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isNegative() const {
|
||||
return !failed() && _data && BN_is_negative(raw());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isPrime(const Context &context = Context()) const {
|
||||
if (failed() || !_data) {
|
||||
return false;
|
||||
}
|
||||
constexpr auto kMillerRabinIterationCount = 30;
|
||||
auto result = BN_is_prime_ex(
|
||||
const auto result = BN_is_prime_ex(
|
||||
raw(),
|
||||
kMillerRabinIterationCount,
|
||||
context.raw(),
|
||||
NULL);
|
||||
nullptr);
|
||||
if (result == 1) {
|
||||
return true;
|
||||
} else if (result != 0) {
|
||||
@@ -204,27 +267,42 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
BN_ULONG modWord(BN_ULONG word) const {
|
||||
Expects(word != 0);
|
||||
BigNum &subWord(unsigned int word) {
|
||||
if (failed()) {
|
||||
return (BN_ULONG)-1;
|
||||
return *this;
|
||||
} else if (!BN_sub_word(raw(), word)) {
|
||||
_failed = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &divWord(BN_ULONG word, BN_ULONG *mod = nullptr) {
|
||||
Expects(word != 0);
|
||||
|
||||
auto result = BN_mod_word(raw(), word);
|
||||
const auto result = failed()
|
||||
? (BN_ULONG)-1
|
||||
: BN_div_word(raw(), word);
|
||||
if (result == (BN_ULONG)-1) {
|
||||
_failed = true;
|
||||
}
|
||||
return result;
|
||||
if (mod) {
|
||||
*mod = result;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
[[nodiscard]] BN_ULONG countModWord(BN_ULONG word) const {
|
||||
Expects(word != 0);
|
||||
|
||||
return failed() ? (BN_ULONG)-1 : BN_mod_word(raw(), word);
|
||||
}
|
||||
|
||||
int bitsSize() const {
|
||||
[[nodiscard]] int bitsSize() const {
|
||||
return failed() ? 0 : BN_num_bits(raw());
|
||||
}
|
||||
int bytesSize() const {
|
||||
[[nodiscard]] int bytesSize() const {
|
||||
return failed() ? 0 : BN_num_bytes(raw());
|
||||
}
|
||||
|
||||
bytes::vector getBytes() const {
|
||||
[[nodiscard]] bytes::vector getBytes() const {
|
||||
if (failed()) {
|
||||
return {};
|
||||
}
|
||||
@@ -237,73 +315,84 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
BIGNUM *raw() {
|
||||
[[nodiscard]] BIGNUM *raw() {
|
||||
if (!_data) _data = BN_new();
|
||||
return _data;
|
||||
}
|
||||
const BIGNUM *raw() const {
|
||||
[[nodiscard]] const BIGNUM *raw() const {
|
||||
if (!_data) _data = BN_new();
|
||||
return _data;
|
||||
}
|
||||
BIGNUM *takeRaw() {
|
||||
return base::take(_data);
|
||||
[[nodiscard]] BIGNUM *takeRaw() {
|
||||
return _failed
|
||||
? nullptr
|
||||
: _data
|
||||
? std::exchange(_data, nullptr)
|
||||
: BN_new();
|
||||
}
|
||||
|
||||
bool failed() const {
|
||||
[[nodiscard]] bool failed() const {
|
||||
return _failed;
|
||||
}
|
||||
|
||||
static BigNum Add(const BigNum &a, const BigNum &b) {
|
||||
BigNum result;
|
||||
result.setAdd(a, b);
|
||||
return result;
|
||||
[[nodiscard]] static BigNum Add(const BigNum &a, const BigNum &b) {
|
||||
return BigNum().setAdd(a, b);
|
||||
}
|
||||
static BigNum Sub(const BigNum &a, const BigNum &b) {
|
||||
BigNum result;
|
||||
result.setSub(a, b);
|
||||
return result;
|
||||
[[nodiscard]] static BigNum Sub(const BigNum &a, const BigNum &b) {
|
||||
return BigNum().setSub(a, b);
|
||||
}
|
||||
static BigNum Mul(
|
||||
[[nodiscard]] static BigNum Mul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const Context &context = Context()) {
|
||||
BigNum result;
|
||||
result.setMul(a, b, context);
|
||||
return result;
|
||||
return BigNum().setMul(a, b, context);
|
||||
}
|
||||
static BigNum ModSub(
|
||||
[[nodiscard]] static BigNum ModAdd(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
BigNum result;
|
||||
result.setModSub(a, b, mod, context);
|
||||
return result;
|
||||
return BigNum().setModAdd(a, b, mod, context);
|
||||
}
|
||||
static BigNum ModMul(
|
||||
[[nodiscard]] static BigNum ModSub(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
BigNum result;
|
||||
result.setModMul(a, b, mod, context);
|
||||
return result;
|
||||
return BigNum().setModSub(a, b, mod, context);
|
||||
}
|
||||
static BigNum ModExp(
|
||||
[[nodiscard]] static BigNum ModMul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModMul(a, b, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModInverse(
|
||||
const BigNum &a,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModInverse(a, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModExp(
|
||||
const BigNum &base,
|
||||
const BigNum &power,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
BigNum result;
|
||||
result.setModExp(base, power, mod, context);
|
||||
return result;
|
||||
return BigNum().setModExp(base, power, mod, context);
|
||||
}
|
||||
static BigNum Failed() {
|
||||
BigNum result;
|
||||
[[nodiscard]] static BigNum Failed() {
|
||||
auto result = BigNum();
|
||||
result._failed = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
BIGNUM *_data = nullptr;
|
||||
void clear() {
|
||||
BN_clear_free(std::exchange(_data, nullptr));
|
||||
}
|
||||
|
||||
mutable BIGNUM *_data = nullptr;
|
||||
mutable bool _failed = false;
|
||||
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <QReadWriteLock>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#elif defined Q_OS_MAC
|
||||
#include <mach/mach_time.h>
|
||||
#else
|
||||
|
||||
@@ -20,6 +20,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/update_checker.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
||||
namespace {
|
||||
|
||||
rpl::producer<TextWithEntities> Text1() {
|
||||
@@ -100,7 +104,7 @@ void AboutBox::showVersionHistory() {
|
||||
}
|
||||
url = url.arg(qsl("talpha%1_%2").arg(cRealAlphaVersion()).arg(Core::countAlphaVersionSignature(cRealAlphaVersion())));
|
||||
|
||||
QApplication::clipboard()->setText(url);
|
||||
QGuiApplication::clipboard()->setText(url);
|
||||
|
||||
Ui::show(Box<InformBox>("The link to the current private alpha version of Telegram Desktop was copied to the clipboard."));
|
||||
} else {
|
||||
|
||||
@@ -11,15 +11,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_profile.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "base/timer.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
struct AbstractBox::LoadingProgress {
|
||||
LoadingProgress(
|
||||
Fn<void()> &&callback,
|
||||
const style::InfiniteRadialAnimation &st);
|
||||
|
||||
Ui::InfiniteRadialAnimation animation;
|
||||
base::Timer removeTimer;
|
||||
};
|
||||
|
||||
AbstractBox::LoadingProgress::LoadingProgress(
|
||||
Fn<void()> &&callback,
|
||||
const style::InfiniteRadialAnimation &st)
|
||||
: animation(std::move(callback), st) {
|
||||
}
|
||||
|
||||
void BoxContent::setTitle(rpl::producer<QString> title) {
|
||||
getDelegate()->setTitle(std::move(title) | Ui::Text::ToWithEntities());
|
||||
}
|
||||
@@ -269,6 +286,8 @@ AbstractBox::AbstractBox(
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
AbstractBox::~AbstractBox() = default;
|
||||
|
||||
void AbstractBox::setLayerType(bool layerType) {
|
||||
_layerType = layerType;
|
||||
updateTitlePosition();
|
||||
@@ -279,15 +298,35 @@ int AbstractBox::titleHeight() const {
|
||||
}
|
||||
|
||||
int AbstractBox::buttonsHeight() const {
|
||||
auto padding = _layerType ? st::boxLayerButtonPadding : st::boxButtonPadding;
|
||||
const auto padding = _layerType
|
||||
? st::boxLayerButtonPadding
|
||||
: st::boxButtonPadding;
|
||||
return padding.top() + st::defaultBoxButton.height + padding.bottom();
|
||||
}
|
||||
|
||||
int AbstractBox::buttonsTop() const {
|
||||
auto padding = _layerType ? st::boxLayerButtonPadding : st::boxButtonPadding;
|
||||
const auto padding = _layerType
|
||||
? st::boxLayerButtonPadding
|
||||
: st::boxButtonPadding;
|
||||
return height() - padding.bottom() - st::defaultBoxButton.height;
|
||||
}
|
||||
|
||||
QRect AbstractBox::loadingRect() const {
|
||||
const auto padding = _layerType
|
||||
? st::boxLayerButtonPadding
|
||||
: st::boxButtonPadding;
|
||||
const auto size = st::boxLoadingSize;
|
||||
const auto skipx = _layerType
|
||||
? st::boxLayerTitlePosition.x()
|
||||
: st::boxTitlePosition.x();
|
||||
const auto skipy = (st::defaultBoxButton.height - size) / 2;
|
||||
return QRect(
|
||||
skipx,
|
||||
height() - padding.bottom() - skipy - size,
|
||||
size,
|
||||
size);
|
||||
}
|
||||
|
||||
void AbstractBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
auto clip = e->rect();
|
||||
@@ -309,6 +348,14 @@ void AbstractBox::paintEvent(QPaintEvent *e) {
|
||||
&& clip.intersects(QRect(0, 0, width(), titleHeight()))) {
|
||||
paintAdditionalTitle(p);
|
||||
}
|
||||
if (_loadingProgress) {
|
||||
const auto rect = loadingRect();
|
||||
_loadingProgress->animation.draw(
|
||||
p,
|
||||
rect.topLeft(),
|
||||
rect.size(),
|
||||
width());
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractBox::paintAdditionalTitle(Painter &p) {
|
||||
@@ -441,6 +488,36 @@ QPointer<Ui::IconButton> AbstractBox::addTopButton(const style::IconButton &st,
|
||||
return result;
|
||||
}
|
||||
|
||||
void AbstractBox::showLoading(bool show) {
|
||||
const auto &st = st::boxLoadingAnimation;
|
||||
if (!show) {
|
||||
if (_loadingProgress && !_loadingProgress->removeTimer.isActive()) {
|
||||
_loadingProgress->removeTimer.callOnce(
|
||||
st.sineDuration + st.sinePeriod);
|
||||
_loadingProgress->animation.stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!_loadingProgress) {
|
||||
const auto callback = [=] {
|
||||
if (!anim::Disabled()) {
|
||||
const auto t = st::boxLoadingAnimation.thickness;
|
||||
update(loadingRect().marginsAdded({ t, t, t, t }));
|
||||
}
|
||||
};
|
||||
_loadingProgress = std::make_unique<LoadingProgress>(
|
||||
callback,
|
||||
st::boxLoadingAnimation);
|
||||
_loadingProgress->removeTimer.setCallback([=] {
|
||||
_loadingProgress = nullptr;
|
||||
});
|
||||
} else {
|
||||
_loadingProgress->removeTimer.cancel();
|
||||
}
|
||||
_loadingProgress->animation.start();
|
||||
}
|
||||
|
||||
|
||||
void AbstractBox::setDimensions(int newWidth, int maxHeight, bool forceCenterPosition) {
|
||||
_maxContentHeight = maxHeight;
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
virtual QPointer<Ui::IconButton> addTopButton(
|
||||
const style::IconButton &st,
|
||||
Fn<void()> clickCallback) = 0;
|
||||
virtual void showLoading(bool show) = 0;
|
||||
virtual void updateButtonsPositions() = 0;
|
||||
|
||||
virtual void showBox(
|
||||
@@ -133,6 +134,9 @@ public:
|
||||
std::move(clickCallback),
|
||||
st);
|
||||
}
|
||||
void showLoading(bool show) {
|
||||
getDelegate()->showLoading(show);
|
||||
}
|
||||
void updateButtonsGeometry() {
|
||||
getDelegate()->updateButtonsPositions();
|
||||
}
|
||||
@@ -271,6 +275,7 @@ public:
|
||||
AbstractBox(
|
||||
not_null<Window::LayerStackWidget*> layer,
|
||||
object_ptr<BoxContent> content);
|
||||
~AbstractBox();
|
||||
|
||||
void parentResized() override;
|
||||
|
||||
@@ -294,6 +299,7 @@ public:
|
||||
QPointer<Ui::IconButton> addTopButton(
|
||||
const style::IconButton &st,
|
||||
Fn<void()> clickCallback) override;
|
||||
void showLoading(bool show) override;
|
||||
void updateButtonsPositions() override;
|
||||
QPointer<QWidget> outerContainer() override;
|
||||
|
||||
@@ -332,17 +338,20 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
struct LoadingProgress;
|
||||
|
||||
void paintAdditionalTitle(Painter &p);
|
||||
void updateTitlePosition();
|
||||
void refreshLang();
|
||||
|
||||
bool hasTitle() const;
|
||||
int titleHeight() const;
|
||||
int buttonsHeight() const;
|
||||
int buttonsTop() const;
|
||||
int contentTop() const;
|
||||
int countFullHeight() const;
|
||||
int countRealHeight() const;
|
||||
[[nodiscard]] bool hasTitle() const;
|
||||
[[nodiscard]] int titleHeight() const;
|
||||
[[nodiscard]] int buttonsHeight() const;
|
||||
[[nodiscard]] int buttonsTop() const;
|
||||
[[nodiscard]] int contentTop() const;
|
||||
[[nodiscard]] int countFullHeight() const;
|
||||
[[nodiscard]] int countRealHeight() const;
|
||||
[[nodiscard]] QRect loadingRect() const;
|
||||
void updateSize();
|
||||
|
||||
not_null<Window::LayerStackWidget*> _layer;
|
||||
@@ -363,6 +372,7 @@ private:
|
||||
std::vector<object_ptr<Ui::RoundButton>> _buttons;
|
||||
object_ptr<Ui::RoundButton> _leftButton = { nullptr };
|
||||
base::unique_qptr<Ui::IconButton> _topButton = { nullptr };
|
||||
std::unique_ptr<LoadingProgress> _loadingProgress;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -41,9 +41,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "observer_peer.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxGroupChannelTitle = 255; // See also edit_peer_info_box.
|
||||
constexpr auto kMaxGroupChannelTitle = 128; // See also edit_peer_info_box.
|
||||
constexpr auto kMaxUserFirstLastName = 64; // See also edit_contact_box.
|
||||
constexpr auto kMaxChannelDescription = 255; // See also edit_peer_info_box.
|
||||
constexpr auto kMinUsernameLength = 5;
|
||||
|
||||
@@ -58,6 +62,48 @@ bool IsValidPhone(QString phone) {
|
||||
|| phone == qsl("4242")));
|
||||
}
|
||||
|
||||
void ChatCreateDone(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
QImage image,
|
||||
const MTPUpdates &updates) {
|
||||
navigation->session().api().applyUpdates(updates);
|
||||
|
||||
auto success = base::make_optional(&updates)
|
||||
| [](auto updates) -> std::optional<const QVector<MTPChat>*> {
|
||||
switch (updates->type()) {
|
||||
case mtpc_updates:
|
||||
return &updates->c_updates().vchats().v;
|
||||
case mtpc_updatesCombined:
|
||||
return &updates->c_updatesCombined().vchats().v;
|
||||
}
|
||||
LOG(("API Error: unexpected update cons %1 "
|
||||
"(GroupInfoBox::creationDone)").arg(updates->type()));
|
||||
return std::nullopt;
|
||||
}
|
||||
| [](auto chats) {
|
||||
return (!chats->empty()
|
||||
&& chats->front().type() == mtpc_chat)
|
||||
? base::make_optional(chats)
|
||||
: std::nullopt;
|
||||
}
|
||||
| [&](auto chats) {
|
||||
return navigation->session().data().chat(
|
||||
chats->front().c_chat().vid().v);
|
||||
}
|
||||
| [&](not_null<ChatData*> chat) {
|
||||
if (!image.isNull()) {
|
||||
chat->session().api().uploadPeerPhoto(
|
||||
chat,
|
||||
std::move(image));
|
||||
}
|
||||
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
|
||||
};
|
||||
if (!success) {
|
||||
LOG(("API Error: chat not found in updates "
|
||||
"(ContactsBox::creationDone)"));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
style::InputField CreateBioFieldStyle() {
|
||||
@@ -525,44 +571,10 @@ void GroupInfoBox::createGroup(
|
||||
MTP_string(title)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
auto image = _photo->takeResultImage();
|
||||
Ui::hideLayer();
|
||||
const auto navigation = _navigation;
|
||||
|
||||
_navigation->session().api().applyUpdates(result);
|
||||
|
||||
auto success = base::make_optional(&result)
|
||||
| [](auto updates) -> std::optional<const QVector<MTPChat>*> {
|
||||
switch (updates->type()) {
|
||||
case mtpc_updates:
|
||||
return &updates->c_updates().vchats().v;
|
||||
case mtpc_updatesCombined:
|
||||
return &updates->c_updatesCombined().vchats().v;
|
||||
}
|
||||
LOG(("API Error: unexpected update cons %1 "
|
||||
"(GroupInfoBox::creationDone)").arg(updates->type()));
|
||||
return std::nullopt;
|
||||
}
|
||||
| [](auto chats) {
|
||||
return (!chats->empty()
|
||||
&& chats->front().type() == mtpc_chat)
|
||||
? base::make_optional(chats)
|
||||
: std::nullopt;
|
||||
}
|
||||
| [&](auto chats) {
|
||||
return _navigation->session().data().chat(
|
||||
chats->front().c_chat().vid().v);
|
||||
}
|
||||
| [&](not_null<ChatData*> chat) {
|
||||
if (!image.isNull()) {
|
||||
chat->session().api().uploadPeerPhoto(
|
||||
chat,
|
||||
std::move(image));
|
||||
}
|
||||
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
|
||||
};
|
||||
if (!success) {
|
||||
LOG(("API Error: chat not found in updates "
|
||||
"(ContactsBox::creationDone)"));
|
||||
}
|
||||
Ui::hideLayer(); // Destroys 'this'.
|
||||
ChatCreateDone(navigation, std::move(image), result);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_creationRequestId = 0;
|
||||
if (error.type() == qstr("NO_CHAT_TITLE")) {
|
||||
@@ -1165,8 +1177,8 @@ void EditNameBox::prepare() {
|
||||
if (_invertOrder) {
|
||||
setTabOrder(_last, _first);
|
||||
}
|
||||
_first->setMaxLength(kMaxGroupChannelTitle);
|
||||
_last->setMaxLength(kMaxGroupChannelTitle);
|
||||
_first->setMaxLength(kMaxUserFirstLastName);
|
||||
_last->setMaxLength(kMaxUserFirstLastName);
|
||||
|
||||
connect(_first, &Ui::InputField::submitted, [=] { submit(); });
|
||||
connect(_last, &Ui::InputField::submitted, [=] { submit(); });
|
||||
|
||||
@@ -329,7 +329,7 @@ void BackgroundBox::Inner::paintPaper(
|
||||
if (paper.data.id() == Window::Theme::Background()->id()) {
|
||||
const auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
const auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
_check->paint(p, crl::now(), checkLeft, checkTop, width());
|
||||
_check->paint(p, checkLeft, checkTop, width());
|
||||
} else if (Data::IsCloudWallPaper(paper.data)
|
||||
&& !Data::IsDefaultWallPaper(paper.data)
|
||||
&& over.has_value()
|
||||
|
||||
@@ -27,6 +27,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxWallPaperSlugLength = 255;
|
||||
@@ -221,7 +224,7 @@ void ServiceCheck::Generator::invalidate() {
|
||||
|
||||
ServiceCheck::Generator &ServiceCheck::Frames() {
|
||||
static const auto Instance = Ui::CreateChild<Generator>(
|
||||
QApplication::instance());
|
||||
QCoreApplication::instance());
|
||||
return *Instance;
|
||||
}
|
||||
|
||||
@@ -284,12 +287,14 @@ AdminLog::OwnedItem GenerateTextItem(
|
||||
const auto flags = Flag::f_entities
|
||||
| Flag::f_from_id
|
||||
| (out ? Flag::f_out : Flag(0));
|
||||
const auto clientFlags = MTPDmessage_ClientFlag::f_fake_history_item;
|
||||
const auto replyTo = 0;
|
||||
const auto viaBotId = 0;
|
||||
const auto item = history->owner().makeMessage(
|
||||
history,
|
||||
++id,
|
||||
flags,
|
||||
clientFlags,
|
||||
replyTo,
|
||||
viaBotId,
|
||||
base::unixtime::now(),
|
||||
@@ -496,7 +501,7 @@ void BackgroundPreviewBox::apply() {
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::share() {
|
||||
QApplication::clipboard()->setText(_paper.shareUrl());
|
||||
QGuiApplication::clipboard()->setText(_paper.shareUrl());
|
||||
Ui::Toast::Show(tr::lng_background_link_copied(tr::now));
|
||||
}
|
||||
|
||||
|
||||
@@ -161,6 +161,12 @@ boxPhotoCompressedSkip: 20px;
|
||||
boxPhotoCaptionSkip: 8px;
|
||||
boxPhotoTextFg: windowSubTextFg;
|
||||
|
||||
boxLoadingAnimation: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) {
|
||||
color: windowSubTextFg;
|
||||
thickness: 2px;
|
||||
}
|
||||
boxLoadingSize: 20px;
|
||||
|
||||
cropPointSize: 10px;
|
||||
cropSkip: 13px;
|
||||
cropMinSize: 20px;
|
||||
@@ -321,12 +327,14 @@ contactsMultiSelect: MultiSelect {
|
||||
}
|
||||
contactsPhotoCheckIcon: icon {{
|
||||
"default_checkbox_check",
|
||||
windowFgActive,
|
||||
overviewCheckFgActive,
|
||||
point(3px, 6px)
|
||||
}};
|
||||
contactsPhotoCheck: RoundCheckbox(defaultRoundCheckbox) {
|
||||
size: 20px;
|
||||
sizeSmall: 0.3;
|
||||
bgInactive: overviewCheckBg;
|
||||
bgActive: overviewCheckBgActive;
|
||||
check: contactsPhotoCheckIcon;
|
||||
}
|
||||
contactsPhotoCheckbox: RoundImageCheckbox {
|
||||
@@ -877,6 +885,21 @@ themesScroll: ScrollArea(defaultScrollArea) {
|
||||
bottomsh: 0px;
|
||||
topsh: 0px;
|
||||
}
|
||||
themesMenuToggle: IconButton(defaultIconButton) {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
|
||||
icon: icon {{ "title_menu_dots", menuIconFg }};
|
||||
iconOver: icon {{ "title_menu_dots", menuIconFgOver }};
|
||||
iconPosition: point(-1px, -1px);
|
||||
|
||||
rippleAreaPosition: point(4px, 4px);
|
||||
rippleAreaSize: 36px;
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgOver;
|
||||
}
|
||||
}
|
||||
themesMenuPosition: point(-2px, 25px);
|
||||
|
||||
createPollField: InputField(defaultInputField) {
|
||||
font: boxTextFont;
|
||||
|
||||
@@ -27,6 +27,11 @@ public:
|
||||
_month.setForced(_month.value(), true);
|
||||
}
|
||||
|
||||
void setBeginningButton(bool enabled);
|
||||
bool hasBeginningButton() const {
|
||||
return _beginningButton;
|
||||
}
|
||||
|
||||
void setMinDate(QDate date);
|
||||
void setMaxDate(QDate date);
|
||||
|
||||
@@ -55,6 +60,9 @@ public:
|
||||
bool isEnabled(int index) const {
|
||||
return (index >= _minDayIndex) && (index <= _maxDayIndex);
|
||||
}
|
||||
bool atBeginning() const {
|
||||
return _highlighted == _min;
|
||||
}
|
||||
|
||||
const base::Variable<QDate> &month() {
|
||||
return _month;
|
||||
@@ -69,6 +77,8 @@ private:
|
||||
static int daysShiftForMonth(QDate month);
|
||||
static int rowsCountForMonth(QDate month);
|
||||
|
||||
bool _beginningButton = false;
|
||||
|
||||
base::Variable<QDate> _month;
|
||||
QDate _min, _max;
|
||||
QDate _highlighted;
|
||||
@@ -86,6 +96,10 @@ CalendarBox::Context::Context(QDate month, QDate highlighted) : _highlighted(hig
|
||||
showMonth(month);
|
||||
}
|
||||
|
||||
void CalendarBox::Context::setBeginningButton(bool enabled) {
|
||||
_beginningButton = enabled;
|
||||
}
|
||||
|
||||
void CalendarBox::Context::setMinDate(QDate date) {
|
||||
_min = date;
|
||||
applyMonth(_month.value(), true);
|
||||
@@ -192,6 +206,7 @@ public:
|
||||
|
||||
int countHeight();
|
||||
void setDateChosenCallback(Fn<void(QDate)> callback);
|
||||
void selectBeginning();
|
||||
|
||||
~Inner();
|
||||
|
||||
@@ -389,7 +404,10 @@ void CalendarBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
auto pressed = _pressed;
|
||||
setPressed(kEmptySelection);
|
||||
if (pressed != kEmptySelection && pressed == _selected) {
|
||||
_dateChosenCallback(_context->dateFromIndex(pressed));
|
||||
crl::on_main(this, [=] {
|
||||
const auto onstack = _dateChosenCallback;
|
||||
onstack(_context->dateFromIndex(pressed));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +435,10 @@ void CalendarBox::Inner::setDateChosenCallback(Fn<void(QDate)> callback) {
|
||||
_dateChosenCallback = std::move(callback);
|
||||
}
|
||||
|
||||
void CalendarBox::Inner::selectBeginning() {
|
||||
_dateChosenCallback(_context->dateFromIndex(_context->minDayIndex()));
|
||||
}
|
||||
|
||||
CalendarBox::Inner::~Inner() = default;
|
||||
|
||||
class CalendarBox::Title : public TWidget, private base::Subscriber {
|
||||
@@ -494,6 +516,14 @@ void CalendarBox::setMaxDate(QDate date) {
|
||||
_context->setMaxDate(date);
|
||||
}
|
||||
|
||||
bool CalendarBox::hasBeginningButton() const {
|
||||
return _context->hasBeginningButton();
|
||||
}
|
||||
|
||||
void CalendarBox::setBeginningButton(bool enabled) {
|
||||
_context->setBeginningButton(enabled);
|
||||
}
|
||||
|
||||
void CalendarBox::prepare() {
|
||||
_previous->setClickedCallback([this] { goPreviousMonth(); });
|
||||
_next->setClickedCallback([this] { goNextMonth(); });
|
||||
@@ -510,6 +540,9 @@ void CalendarBox::prepare() {
|
||||
if (_finalize) {
|
||||
_finalize(this);
|
||||
}
|
||||
if (!_context->atBeginning() && hasBeginningButton()) {
|
||||
addLeftButton(tr::lng_calendar_beginning(), [this] { _inner->selectBeginning(); });
|
||||
}
|
||||
}
|
||||
|
||||
bool CalendarBox::isPreviousEnabled() const {
|
||||
@@ -555,6 +588,8 @@ void CalendarBox::resizeEvent(QResizeEvent *e) {
|
||||
void CalendarBox::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Escape) {
|
||||
e->ignore();
|
||||
} else if (e->key() == Qt::Key_Home) {
|
||||
_inner->selectBeginning();
|
||||
} else if (e->key() == Qt::Key_Left) {
|
||||
goPreviousMonth();
|
||||
} else if (e->key() == Qt::Key_Right) {
|
||||
|
||||
@@ -33,6 +33,8 @@ public:
|
||||
FnMut<void(not_null<CalendarBox*>)> finalize,
|
||||
const style::CalendarSizes &st);
|
||||
|
||||
void setBeginningButton(bool enabled);
|
||||
bool hasBeginningButton() const;
|
||||
|
||||
void setMinDate(QDate date);
|
||||
void setMaxDate(QDate date);
|
||||
|
||||
@@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/click_handler_types.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "data/data_scheduled_messages.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_channel.h"
|
||||
@@ -34,6 +35,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "observer_peer.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
TextParseOptions _confirmBoxTextOptions = {
|
||||
TextParseLinks | TextParseMultiline | TextParseMarkdown | TextParseRichText, // flags
|
||||
0, // maxw
|
||||
@@ -542,10 +546,11 @@ void DeleteMessagesBox::prepare() {
|
||||
: tr::lng_selected_delete_sure(tr::now, lt_count, _ids.size());
|
||||
if (const auto peer = checkFromSinglePeer()) {
|
||||
auto count = int(_ids.size());
|
||||
if (auto revoke = revokeText(peer)) {
|
||||
if (hasScheduledMessages()) {
|
||||
} else if (auto revoke = revokeText(peer)) {
|
||||
_revoke.create(this, revoke->checkbox, false, st::defaultBoxCheckbox);
|
||||
appendDetails(std::move(revoke->description));
|
||||
} else if (peer && peer->isChannel()) {
|
||||
} else if (peer->isChannel()) {
|
||||
if (peer->isMegagroup()) {
|
||||
appendDetails({ tr::lng_delete_for_everyone_hint(tr::now, lt_count, count) });
|
||||
}
|
||||
@@ -580,6 +585,17 @@ void DeleteMessagesBox::prepare() {
|
||||
setDimensions(st::boxWidth, fullHeight);
|
||||
}
|
||||
|
||||
bool DeleteMessagesBox::hasScheduledMessages() const {
|
||||
for (const auto fullId : std::as_const(_ids)) {
|
||||
if (const auto item = _session->data().message(fullId)) {
|
||||
if (item->isScheduled()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PeerData *DeleteMessagesBox::checkFromSinglePeer() const {
|
||||
auto result = (PeerData*)nullptr;
|
||||
for (const auto fullId : std::as_const(_ids)) {
|
||||
@@ -765,9 +781,21 @@ void DeleteMessagesBox::deleteAndClear() {
|
||||
}
|
||||
|
||||
base::flat_map<not_null<PeerData*>, QVector<MTPint>> idsByPeer;
|
||||
base::flat_map<not_null<PeerData*>, QVector<MTPint>> scheduledIdsByPeer;
|
||||
for (const auto itemId : _ids) {
|
||||
if (const auto item = _session->data().message(itemId)) {
|
||||
const auto history = item->history();
|
||||
if (item->isScheduled()) {
|
||||
const auto wasOnServer = !item->isSending()
|
||||
&& !item->hasFailed();
|
||||
if (wasOnServer) {
|
||||
scheduledIdsByPeer[history->peer].push_back(MTP_int(
|
||||
_session->data().scheduledMessages().lookupId(item)));
|
||||
} else {
|
||||
_session->data().scheduledMessages().removeSending(item);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const auto wasOnServer = IsServerMsgId(item->id);
|
||||
const auto wasLast = (history->lastMessage() == item);
|
||||
const auto wasInChats = (history->chatListMessage() == item);
|
||||
@@ -784,6 +812,15 @@ void DeleteMessagesBox::deleteAndClear() {
|
||||
for (const auto &[peer, ids] : idsByPeer) {
|
||||
peer->session().api().deleteMessages(peer, ids, revoke);
|
||||
}
|
||||
for (const auto &[peer, ids] : scheduledIdsByPeer) {
|
||||
peer->session().api().request(MTPmessages_DeleteScheduledMessages(
|
||||
peer->input,
|
||||
MTP_vector<MTPint>(ids)
|
||||
)).done([=, peer=peer](const MTPUpdates &updates) {
|
||||
peer->session().api().applyUpdates(updates);
|
||||
}).send();
|
||||
}
|
||||
|
||||
const auto session = _session;
|
||||
Ui::hideLayer();
|
||||
session->data().sendHistoryChangeNotifications();
|
||||
|
||||
@@ -176,8 +176,10 @@ private:
|
||||
TextWithEntities description;
|
||||
};
|
||||
void deleteAndClear();
|
||||
PeerData *checkFromSinglePeer() const;
|
||||
std::optional<RevokeConfig> revokeText(not_null<PeerData*> peer) const;
|
||||
[[nodiscard]] PeerData *checkFromSinglePeer() const;
|
||||
[[nodiscard]] bool hasScheduledMessages() const;
|
||||
[[nodiscard]] std::optional<RevokeConfig> revokeText(
|
||||
not_null<PeerData*> peer) const;
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_info.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000);
|
||||
@@ -1426,7 +1429,7 @@ void ProxiesBoxController::share(const ProxyData &proxy) {
|
||||
? "&pass=" + qthelp::url_encode(proxy.password) : "")
|
||||
+ ((proxy.type == Type::Mtproto && !proxy.password.isEmpty())
|
||||
? "&secret=" + proxy.password : "");
|
||||
QApplication::clipboard()->setText(link);
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
Ui::Toast::Show(tr::lng_username_copied(tr::now));
|
||||
}
|
||||
|
||||
@@ -1434,7 +1437,7 @@ ProxiesBoxController::~ProxiesBoxController() {
|
||||
if (_saveTimer.isActive()) {
|
||||
App::CallDelayed(
|
||||
kSaveSettingsDelayedTimeout,
|
||||
QApplication::instance(),
|
||||
QCoreApplication::instance(),
|
||||
[] { Local::writeSettings(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "core/event_filter.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "history/view/history_view_schedule_box.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "base/unique_qptr.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -521,11 +523,11 @@ void Options::addEmptyOption() {
|
||||
Core::InstallEventFilter(field, [=](not_null<QEvent*> event) {
|
||||
if (event->type() != QEvent::KeyPress
|
||||
|| !field->getLastText().isEmpty()) {
|
||||
return false;
|
||||
return Core::EventFilter::Result::Continue;
|
||||
}
|
||||
const auto key = static_cast<QKeyEvent*>(event.get())->key();
|
||||
if (key != Qt::Key_Backspace) {
|
||||
return false;
|
||||
return Core::EventFilter::Result::Continue;
|
||||
}
|
||||
|
||||
const auto index = findField(field);
|
||||
@@ -534,7 +536,7 @@ void Options::addEmptyOption() {
|
||||
} else {
|
||||
_backspaceInFront.fire({});
|
||||
}
|
||||
return true;
|
||||
return Core::EventFilter::Result::Cancel;
|
||||
});
|
||||
|
||||
_list.back().removeClicks(
|
||||
@@ -592,11 +594,15 @@ void Options::checkLastOption() {
|
||||
|
||||
} // namespace
|
||||
|
||||
CreatePollBox::CreatePollBox(QWidget*, not_null<Main::Session*> session)
|
||||
: _session(session) {
|
||||
CreatePollBox::CreatePollBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
Api::SendType sendType)
|
||||
: _session(session)
|
||||
, _sendType(sendType) {
|
||||
}
|
||||
|
||||
rpl::producer<PollData> CreatePollBox::submitRequests() const {
|
||||
rpl::producer<CreatePollBox::Result> CreatePollBox::submitRequests() const {
|
||||
return _submitRequests.events();
|
||||
}
|
||||
|
||||
@@ -703,6 +709,22 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
result.answers = options->toPollAnswers();
|
||||
return result;
|
||||
};
|
||||
const auto send = [=](Api::SendOptions options) {
|
||||
_submitRequests.fire({ collectResult(), options });
|
||||
};
|
||||
const auto sendSilent = [=] {
|
||||
auto options = Api::SendOptions();
|
||||
options.silent = true;
|
||||
send(options);
|
||||
};
|
||||
const auto sendScheduled = [=] {
|
||||
Ui::show(
|
||||
HistoryView::PrepareScheduleBox(
|
||||
this,
|
||||
SendMenuType::Scheduled,
|
||||
send),
|
||||
LayerOption::KeepOther);
|
||||
};
|
||||
const auto updateValid = [=] {
|
||||
valid->fire(isValidQuestion() && options->isValid());
|
||||
};
|
||||
@@ -715,9 +737,16 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
) | rpl::start_with_next([=](bool valid) {
|
||||
clearButtons();
|
||||
if (valid) {
|
||||
addButton(
|
||||
const auto submit = addButton(
|
||||
tr::lng_polls_create_button(),
|
||||
[=] { _submitRequests.fire(collectResult()); });
|
||||
[=] { send({}); });
|
||||
if (_sendType == Api::SendType::Normal) {
|
||||
SetupSendMenu(
|
||||
submit.data(),
|
||||
[=] { return SendMenuType::Scheduled; },
|
||||
sendSilent,
|
||||
sendScheduled);
|
||||
}
|
||||
}
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
}, lifetime());
|
||||
|
||||
@@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "api/api_common.h"
|
||||
#include "data/data_poll.h"
|
||||
|
||||
struct PollData;
|
||||
|
||||
@@ -21,9 +23,17 @@ class Session;
|
||||
|
||||
class CreatePollBox : public BoxContent {
|
||||
public:
|
||||
CreatePollBox(QWidget*, not_null<Main::Session*> session);
|
||||
struct Result {
|
||||
PollData poll;
|
||||
Api::SendOptions options;
|
||||
};
|
||||
|
||||
rpl::producer<PollData> submitRequests() const;
|
||||
CreatePollBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
Api::SendType sendType);
|
||||
|
||||
rpl::producer<Result> submitRequests() const;
|
||||
void submitFailed(const QString &error);
|
||||
|
||||
void setInnerFocus() override;
|
||||
@@ -37,8 +47,9 @@ private:
|
||||
not_null<Ui::VerticalLayout*> container);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
const Api::SendType _sendType = Api::SendType();
|
||||
Fn<void()> _setInnerFocus;
|
||||
Fn<rpl::producer<bool>()> _dataIsValidValue;
|
||||
rpl::event_stream<PollData> _submitRequests;
|
||||
rpl::event_stream<Result> _submitRequests;
|
||||
|
||||
};
|
||||
|
||||
@@ -38,6 +38,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "confirm_box.h"
|
||||
|
||||
#include <QtCore/QMimeData>
|
||||
|
||||
EditCaptionBox::EditCaptionBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
@@ -277,14 +279,13 @@ EditCaptionBox::EditCaptionBox(
|
||||
}, _wayWrap->lifetime());
|
||||
}
|
||||
|
||||
bool EditCaptionBox::emojiFilter(not_null<QEvent*> event) {
|
||||
void EditCaptionBox::emojiFilterForGeometry(not_null<QEvent*> event) {
|
||||
const auto type = event->type();
|
||||
if (type == QEvent::Move || type == QEvent::Resize) {
|
||||
// updateEmojiPanelGeometry uses not only container geometry, but
|
||||
// also container children geometries that will be updated later.
|
||||
crl::on_main(this, [=] { updateEmojiPanelGeometry(); });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditCaptionBox::updateEmojiPanelGeometry() {
|
||||
@@ -711,14 +712,16 @@ void EditCaptionBox::setupEmojiPanel() {
|
||||
st::emojiPanMinHeight / 2,
|
||||
st::emojiPanMinHeight);
|
||||
_emojiPanel->hide();
|
||||
_emojiPanel->getSelector()->emojiChosen(
|
||||
_emojiPanel->selector()->emojiChosen(
|
||||
) | rpl::start_with_next([=](EmojiPtr emoji) {
|
||||
Ui::InsertEmojiAtCursor(_field->textCursor(), emoji);
|
||||
}, lifetime());
|
||||
|
||||
_emojiFilter.reset(Core::InstallEventFilter(
|
||||
container,
|
||||
[=](not_null<QEvent*> event) { return emojiFilter(event); }));
|
||||
const auto filterCallback = [=](not_null<QEvent*> event) {
|
||||
emojiFilterForGeometry(event);
|
||||
return Core::EventFilter::Result::Continue;
|
||||
};
|
||||
_emojiFilter.reset(Core::InstallEventFilter(container, filterCallback));
|
||||
|
||||
_emojiToggle.create(this, st::boxAttachEmoji);
|
||||
_emojiToggle->installEventFilter(_emojiPanel);
|
||||
@@ -920,7 +923,7 @@ void EditCaptionBox::save() {
|
||||
std::move(_preparedList),
|
||||
(!_asFile && _photo) ? SendMediaType::Photo : SendMediaType::File,
|
||||
_field->getTextWithAppliedMarkdown(),
|
||||
ApiWrap::SendOptions(item->history()),
|
||||
Api::SendAction(item->history()),
|
||||
item->fullId().msg);
|
||||
closeBox();
|
||||
return;
|
||||
@@ -934,7 +937,8 @@ void EditCaptionBox::save() {
|
||||
MTP_string(sending.text),
|
||||
MTPInputMedia(),
|
||||
MTPReplyMarkup(),
|
||||
sentEntities),
|
||||
sentEntities,
|
||||
MTP_int(0)), // schedule_date
|
||||
rpcDone(&EditCaptionBox::saveDone),
|
||||
rpcFail(&EditCaptionBox::saveFail));
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ private:
|
||||
|
||||
void setupEmojiPanel();
|
||||
void updateEmojiPanelGeometry();
|
||||
bool emojiFilter(not_null<QEvent*> event);
|
||||
void emojiFilterForGeometry(not_null<QEvent*> event);
|
||||
|
||||
void save();
|
||||
void captionResized();
|
||||
|
||||
@@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class EditColorBox::Picker : public TWidget {
|
||||
public:
|
||||
Picker(QWidget *parent, QColor color);
|
||||
Picker(QWidget *parent, Mode mode, QColor color);
|
||||
|
||||
float64 valueX() const {
|
||||
return _x;
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
base::Observable<void> &changed() {
|
||||
return _changed;
|
||||
}
|
||||
void setHSV(int hue, int saturation, int brightness);
|
||||
void setHSB(HSB hsb);
|
||||
void setRGB(int red, int green, int blue);
|
||||
|
||||
protected:
|
||||
@@ -43,8 +43,11 @@ private:
|
||||
QCursor generateCursor();
|
||||
|
||||
void preparePalette();
|
||||
void preparePaletteRGBA();
|
||||
void preparePaletteHSL();
|
||||
void updateCurrentPoint(QPoint localPosition);
|
||||
|
||||
Mode _mode;
|
||||
QColor _topleft;
|
||||
QColor _topright;
|
||||
QColor _bottomleft;
|
||||
@@ -84,7 +87,9 @@ QCursor EditColorBox::Picker::generateCursor() {
|
||||
return QCursor(QPixmap::fromImage(cursor));
|
||||
}
|
||||
|
||||
EditColorBox::Picker::Picker(QWidget *parent, QColor color) : TWidget(parent) {
|
||||
EditColorBox::Picker::Picker(QWidget *parent, Mode mode, QColor color)
|
||||
: TWidget(parent)
|
||||
, _mode(mode) {
|
||||
setCursor(generateCursor());
|
||||
|
||||
auto size = QSize(st::colorPickerSize, st::colorPickerSize);
|
||||
@@ -137,18 +142,27 @@ void EditColorBox::Picker::preparePalette() {
|
||||
if (!_paletteInvalidated) return;
|
||||
_paletteInvalidated = false;
|
||||
|
||||
auto size = _palette.width();
|
||||
if (_mode == Mode::RGBA) {
|
||||
preparePaletteRGBA();
|
||||
} else {
|
||||
preparePaletteHSL();
|
||||
}
|
||||
_palette.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::preparePaletteRGBA() {
|
||||
const auto size = _palette.width();
|
||||
auto ints = reinterpret_cast<uint32*>(_palette.bits());
|
||||
auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
|
||||
const auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
|
||||
|
||||
constexpr auto Large = 1024 * 1024;
|
||||
constexpr auto LargeBit = 20; // n / Large == (n >> LargeBit)
|
||||
auto part = Large / size;
|
||||
const auto part = Large / size;
|
||||
|
||||
auto topleft = anim::shifted(_topleft);
|
||||
auto topright = anim::shifted(_topright);
|
||||
auto bottomleft = anim::shifted(_bottomleft);
|
||||
auto bottomright = anim::shifted(_bottomright);
|
||||
const auto topleft = anim::shifted(_topleft);
|
||||
const auto topright = anim::shifted(_topright);
|
||||
const auto bottomleft = anim::shifted(_bottomleft);
|
||||
const auto bottomright = anim::shifted(_bottomright);
|
||||
|
||||
auto y_accumulated = 0;
|
||||
for (auto y = 0; y != size; ++y, y_accumulated += part) {
|
||||
@@ -156,11 +170,11 @@ void EditColorBox::Picker::preparePalette() {
|
||||
// 0 <= y_accumulated < Large
|
||||
// 0 <= y_ratio < 256
|
||||
|
||||
auto top_ratio = 255 - y_ratio;
|
||||
auto bottom_ratio = y_ratio;
|
||||
const auto top_ratio = 256 - y_ratio;
|
||||
const auto bottom_ratio = y_ratio;
|
||||
|
||||
auto left = anim::reshifted(bottomleft * bottom_ratio + topleft * top_ratio);
|
||||
auto right = anim::reshifted(bottomright * bottom_ratio + topright * top_ratio);
|
||||
const auto left = anim::reshifted(bottomleft * bottom_ratio + topleft * top_ratio);
|
||||
const auto right = anim::reshifted(bottomright * bottom_ratio + topright * top_ratio);
|
||||
|
||||
auto x_accumulated = 0;
|
||||
for (auto x = 0; x != size; ++x, x_accumulated += part) {
|
||||
@@ -168,7 +182,7 @@ void EditColorBox::Picker::preparePalette() {
|
||||
// 0 <= x_accumulated < Large
|
||||
// 0 <= x_ratio < 256
|
||||
|
||||
auto left_ratio = 255 - x_ratio;
|
||||
auto left_ratio = 256 - x_ratio;
|
||||
auto right_ratio = x_ratio;
|
||||
|
||||
*ints++ = anim::unshifted(left * left_ratio + right * right_ratio);
|
||||
@@ -177,6 +191,40 @@ void EditColorBox::Picker::preparePalette() {
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::preparePaletteHSL() {
|
||||
const auto size = _palette.width();
|
||||
const auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
|
||||
auto ints = reinterpret_cast<uint32*>(_palette.bits());
|
||||
|
||||
constexpr auto Large = 1024 * 1024;
|
||||
constexpr auto LargeBit = 20; // n / Large == (n >> LargeBit)
|
||||
const auto part = Large / size;
|
||||
|
||||
const auto lightness = _topleft.lightness();
|
||||
const auto right = anim::shifted(_bottomright);
|
||||
|
||||
for (auto y = 0; y != size; ++y) {
|
||||
const auto hue = y * 360 / size;
|
||||
const auto color = QColor::fromHsl(hue, 255, lightness).toRgb();
|
||||
const auto left = anim::shifted(anim::getPremultiplied(color));
|
||||
|
||||
auto x_accumulated = 0;
|
||||
for (auto x = 0; x != size; ++x, x_accumulated += part) {
|
||||
auto x_ratio = x_accumulated >> (LargeBit - 8); // (x_accumulated * 256) / Large;
|
||||
// 0 <= x_accumulated < Large
|
||||
// 0 <= x_ratio < 256
|
||||
|
||||
auto left_ratio = 256 - x_ratio;
|
||||
auto right_ratio = x_ratio;
|
||||
*ints++ = anim::unshifted(left * left_ratio + right * right_ratio);
|
||||
}
|
||||
ints += intsAddPerLine;
|
||||
}
|
||||
|
||||
_palette = std::move(_palette).transformed(
|
||||
QTransform(0, 1, 1, 0, 0, 0));
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::updateCurrentPoint(QPoint localPosition) {
|
||||
auto x = snap(localPosition.x(), 0, width()) / float64(width());
|
||||
auto y = snap(localPosition.y(), 0, height()) / float64(height());
|
||||
@@ -188,17 +236,25 @@ void EditColorBox::Picker::updateCurrentPoint(QPoint localPosition) {
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::setHSV(int hue, int saturation, int brightness) {
|
||||
_topleft = QColor(255, 255, 255);
|
||||
_topright.setHsv(qMax(0, hue), 255, 255);
|
||||
_topright = _topright.toRgb();
|
||||
_bottomleft = _bottomright = QColor(0, 0, 0);
|
||||
void EditColorBox::Picker::setHSB(HSB hsb) {
|
||||
if (_mode == Mode::RGBA) {
|
||||
_topleft = QColor(255, 255, 255);
|
||||
_topright.setHsv(qMax(0, hsb.hue), 255, 255);
|
||||
_topright = _topright.toRgb();
|
||||
_bottomleft = _bottomright = QColor(0, 0, 0);
|
||||
|
||||
_x = snap(hsb.saturation / 255., 0., 1.);
|
||||
_y = 1. - snap(hsb.brightness / 255., 0., 1.);
|
||||
} else {
|
||||
_topleft = _topright = QColor::fromHsl(0, 255, hsb.brightness);
|
||||
_bottomleft = _bottomright = QColor::fromHsl(0, 0, hsb.brightness);
|
||||
|
||||
_x = snap(hsb.hue / 360., 0., 1.);
|
||||
_y = 1. - snap(hsb.saturation / 255., 0., 1.);
|
||||
}
|
||||
|
||||
_paletteInvalidated = true;
|
||||
update();
|
||||
|
||||
_x = snap(saturation / 255., 0., 1.);
|
||||
_y = 1. - snap(brightness / 255., 0., 1.);
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::setRGB(int red, int green, int blue) {
|
||||
@@ -206,7 +262,11 @@ void EditColorBox::Picker::setRGB(int red, int green, int blue) {
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::setFromColor(QColor color) {
|
||||
setHSV(color.hsvHue(), color.hsvSaturation(), color.value());
|
||||
if (_mode == Mode::RGBA) {
|
||||
setHSB({ color.hsvHue(), color.hsvSaturation(), color.value() });
|
||||
} else {
|
||||
setHSB({ color.hslHue(), color.hslSaturation(), color.lightness() });
|
||||
}
|
||||
}
|
||||
|
||||
class EditColorBox::Slider : public TWidget {
|
||||
@@ -218,6 +278,7 @@ public:
|
||||
enum class Type {
|
||||
Hue,
|
||||
Opacity,
|
||||
Lightness
|
||||
};
|
||||
Slider(QWidget *parent, Direction direction, Type type, QColor color);
|
||||
|
||||
@@ -231,10 +292,12 @@ public:
|
||||
_value = snap(value, 0., 1.);
|
||||
update();
|
||||
}
|
||||
void setHSV(int hue, int saturation, int brightness);
|
||||
void setHSB(HSB hsb);
|
||||
void setRGB(int red, int green, int blue);
|
||||
void setAlpha(int alpha);
|
||||
|
||||
void setLightnessLimits(int min, int max);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
@@ -254,10 +317,14 @@ private:
|
||||
void generatePixmap();
|
||||
void updatePixmapFromMask();
|
||||
void updateCurrentPoint(QPoint localPosition);
|
||||
[[nodiscard]] QColor applyLimits(QColor color) const;
|
||||
|
||||
Direction _direction = Direction::Horizontal;
|
||||
Type _type = Type::Hue;
|
||||
|
||||
int _lightnessMin = 0;
|
||||
int _lightnessMax = 255;
|
||||
|
||||
QColor _color;
|
||||
float64 _value = 0;
|
||||
|
||||
@@ -270,12 +337,17 @@ private:
|
||||
|
||||
};
|
||||
|
||||
EditColorBox::Slider::Slider(QWidget *parent, Direction direction, Type type, QColor color) : TWidget(parent)
|
||||
EditColorBox::Slider::Slider(
|
||||
QWidget *parent,
|
||||
Direction direction,
|
||||
Type type,
|
||||
QColor color)
|
||||
: TWidget(parent)
|
||||
, _direction(direction)
|
||||
, _type(type)
|
||||
, _color(color.red(), color.green(), color.blue())
|
||||
, _value(valueFromColor(color))
|
||||
, _transparent((_type == Type::Hue) ? QBrush() : style::transparentPlaceholderBrush()) {
|
||||
, _transparent((_type == Type::Opacity) ? style::transparentPlaceholderBrush() : QBrush()) {
|
||||
prepareMinSize();
|
||||
}
|
||||
|
||||
@@ -336,10 +408,9 @@ void EditColorBox::Slider::generatePixmap() {
|
||||
auto part = Large / size;
|
||||
|
||||
if (_type == Type::Hue) {
|
||||
QColor color;
|
||||
for (auto x = 0; x != size; ++x) {
|
||||
color.setHsv(x * 360 / size, 255, 255);
|
||||
auto value = anim::getPremultiplied(color.toRgb());
|
||||
const auto color = QColor::fromHsv(x * 360 / size, 255, 255);
|
||||
const auto value = anim::getPremultiplied(color.toRgb());
|
||||
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
|
||||
ints[y * intsPerLine] = value;
|
||||
}
|
||||
@@ -349,7 +420,7 @@ void EditColorBox::Slider::generatePixmap() {
|
||||
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
|
||||
}
|
||||
_pixmap = App::pixmapFromImageInPlace(std::move(image));
|
||||
} else {
|
||||
} else if (_type == Type::Opacity) {
|
||||
auto color = anim::shifted(QColor(255, 255, 255, 255));
|
||||
auto transparent = anim::shifted(QColor(255, 255, 255, 0));
|
||||
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
|
||||
@@ -359,7 +430,7 @@ void EditColorBox::Slider::generatePixmap() {
|
||||
// 0 <= x_accumulated < Large
|
||||
// 0 <= x_ratio < 256
|
||||
|
||||
*ints++ = anim::unshifted(color * x_ratio + transparent * (255 - x_ratio));
|
||||
*ints++ = anim::unshifted(color * x_ratio + transparent * (256 - x_ratio));
|
||||
}
|
||||
ints += intsPerLineAdded;
|
||||
}
|
||||
@@ -368,22 +439,45 @@ void EditColorBox::Slider::generatePixmap() {
|
||||
}
|
||||
_mask = std::move(image);
|
||||
updatePixmapFromMask();
|
||||
} else {
|
||||
const auto range = _lightnessMax - _lightnessMin;
|
||||
for (auto x = 0; x != size; ++x) {
|
||||
const auto color = QColor::fromHsl(
|
||||
_color.hslHue(),
|
||||
_color.hslSaturation(),
|
||||
_lightnessMin + x * range / size);
|
||||
const auto value = anim::getPremultiplied(color.toRgb());
|
||||
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
|
||||
ints[y * intsPerLine] = value;
|
||||
}
|
||||
++ints;
|
||||
}
|
||||
if (!isHorizontal()) {
|
||||
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
|
||||
}
|
||||
_pixmap = App::pixmapFromImageInPlace(std::move(image));
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::Slider::setHSV(int hue, int saturation, int brightness) {
|
||||
void EditColorBox::Slider::setHSB(HSB hsb) {
|
||||
if (_type == Type::Hue) {
|
||||
// hue == 360 converts to 0 if done in general way
|
||||
_value = valueFromHue(hue);
|
||||
_value = valueFromHue(hsb.hue);
|
||||
update();
|
||||
} else if (_type == Type::Opacity) {
|
||||
_color.setHsv(hsb.hue, hsb.saturation, hsb.brightness);
|
||||
colorUpdated();
|
||||
} else {
|
||||
_color.setHsv(hue, saturation, brightness);
|
||||
_color.setHsl(
|
||||
hsb.hue,
|
||||
hsb.saturation,
|
||||
std::clamp(hsb.brightness, _lightnessMin, _lightnessMax));
|
||||
colorUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::Slider::setRGB(int red, int green, int blue) {
|
||||
_color.setRgb(red, green, blue);
|
||||
_color = applyLimits(QColor(red, green, blue));
|
||||
colorUpdated();
|
||||
}
|
||||
|
||||
@@ -392,12 +486,23 @@ void EditColorBox::Slider::colorUpdated() {
|
||||
_value = valueFromColor(_color);
|
||||
} else if (!_mask.isNull()) {
|
||||
updatePixmapFromMask();
|
||||
} else {
|
||||
_value = valueFromColor(_color);
|
||||
generatePixmap();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
float64 EditColorBox::Slider::valueFromColor(QColor color) const {
|
||||
return (_type == Type::Hue) ? valueFromHue(color.hsvHue()) : color.alphaF();
|
||||
return (_type == Type::Hue)
|
||||
? valueFromHue(color.hsvHue())
|
||||
: (_type == Type::Opacity)
|
||||
? color.alphaF()
|
||||
: std::clamp(
|
||||
((color.lightness() - _lightnessMin)
|
||||
/ float64(_lightnessMax - _lightnessMin)),
|
||||
0.,
|
||||
1.);
|
||||
}
|
||||
|
||||
float64 EditColorBox::Slider::valueFromHue(int hue) const {
|
||||
@@ -411,6 +516,15 @@ void EditColorBox::Slider::setAlpha(int alpha) {
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::Slider::setLightnessLimits(int min, int max) {
|
||||
Expects(max > min);
|
||||
|
||||
_lightnessMin = min;
|
||||
_lightnessMax = max;
|
||||
_color = applyLimits(_color);
|
||||
colorUpdated();
|
||||
}
|
||||
|
||||
void EditColorBox::Slider::updatePixmapFromMask() {
|
||||
_pixmap = App::pixmapFromImageInPlace(style::colorizeImage(_mask, _color));
|
||||
}
|
||||
@@ -426,6 +540,19 @@ void EditColorBox::Slider::updateCurrentPoint(QPoint localPosition) {
|
||||
}
|
||||
}
|
||||
|
||||
QColor EditColorBox::Slider::applyLimits(QColor color) const {
|
||||
if (_type != Type::Lightness) {
|
||||
return color;
|
||||
}
|
||||
|
||||
const auto lightness = color.lightness();
|
||||
const auto clamped = std::clamp(lightness, _lightnessMin, _lightnessMax);
|
||||
if (clamped == lightness) {
|
||||
return color;
|
||||
}
|
||||
return QColor::fromHsl(color.hslHue(), color.hslSaturation(), clamped);
|
||||
}
|
||||
|
||||
class EditColorBox::Field : public Ui::MaskedInputField {
|
||||
public:
|
||||
Field(QWidget *parent, const style::InputField &st, const QString &placeholder, int limit, const QString &units = QString());
|
||||
@@ -613,14 +740,18 @@ void EditColorBox::ResultField::paintAdditionalPlaceholder(Painter &p) {
|
||||
p.drawText(QRect(_st.textMargins.right(), _st.textMargins.top(), width(), height() - _st.textMargins.top() - _st.textMargins.bottom()), "#", style::al_topleft);
|
||||
}
|
||||
|
||||
EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : BoxContent()
|
||||
EditColorBox::EditColorBox(
|
||||
QWidget*,
|
||||
const QString &title,
|
||||
Mode mode,
|
||||
QColor current)
|
||||
: BoxContent()
|
||||
, _title(title)
|
||||
, _picker(this, current)
|
||||
, _hueSlider(this, Slider::Direction::Vertical, Slider::Type::Hue, current)
|
||||
, _opacitySlider(this, Slider::Direction::Horizontal, Slider::Type::Opacity, current)
|
||||
, _mode(mode)
|
||||
, _picker(this, mode, current)
|
||||
, _hueField(this, st::colorValueInput, "H", 360, QString() + QChar(176)) // degree character
|
||||
, _saturationField(this, st::colorValueInput, "S", 100, "%")
|
||||
, _brightnessField(this, st::colorValueInput, "B", 100, "%")
|
||||
, _brightnessField(this, st::colorValueInput, (mode == Mode::RGBA) ? "B" : "L", 100, "%")
|
||||
, _redField(this, st::colorValueInput, "R", 255)
|
||||
, _greenField(this, st::colorValueInput, "G", 255)
|
||||
, _blueField(this, st::colorValueInput, "B", 255)
|
||||
@@ -628,16 +759,47 @@ EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : Box
|
||||
, _transparent(style::transparentPlaceholderBrush())
|
||||
, _current(current)
|
||||
, _new(current) {
|
||||
if (_mode == Mode::RGBA) {
|
||||
_hueSlider.create(
|
||||
this,
|
||||
Slider::Direction::Vertical,
|
||||
Slider::Type::Hue,
|
||||
current);
|
||||
_opacitySlider.create(
|
||||
this,
|
||||
Slider::Direction::Horizontal,
|
||||
Slider::Type::Opacity,
|
||||
current);
|
||||
} else if (_mode == Mode::HSL) {
|
||||
_lightnessSlider.create(
|
||||
this,
|
||||
Slider::Direction::Horizontal,
|
||||
Slider::Type::Lightness,
|
||||
current);
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::setLightnessLimits(int min, int max) {
|
||||
Expects(_mode == Mode::HSL);
|
||||
|
||||
_lightnessMin = min;
|
||||
_lightnessMax = max;
|
||||
_lightnessSlider->setLightnessLimits(min, max);
|
||||
|
||||
const auto adjusted = applyLimits(_new);
|
||||
if (_new != adjusted) {
|
||||
updateFromColor(adjusted);
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::prepare() {
|
||||
setTitle(rpl::single(_title));
|
||||
|
||||
const auto hsvChanged = [=] { updateFromHSVFields(); };
|
||||
const auto hsbChanged = [=] { updateFromHSBFields(); };
|
||||
const auto rgbChanged = [=] { updateFromRGBFields(); };
|
||||
connect(_hueField, &Ui::MaskedInputField::changed, hsvChanged);
|
||||
connect(_saturationField, &Ui::MaskedInputField::changed, hsvChanged);
|
||||
connect(_brightnessField, &Ui::MaskedInputField::changed, hsvChanged);
|
||||
connect(_hueField, &Ui::MaskedInputField::changed, hsbChanged);
|
||||
connect(_saturationField, &Ui::MaskedInputField::changed, hsbChanged);
|
||||
connect(_brightnessField, &Ui::MaskedInputField::changed, hsbChanged);
|
||||
connect(_redField, &Ui::MaskedInputField::changed, rgbChanged);
|
||||
connect(_greenField, &Ui::MaskedInputField::changed, rgbChanged);
|
||||
connect(_blueField, &Ui::MaskedInputField::changed, rgbChanged);
|
||||
@@ -661,8 +823,15 @@ void EditColorBox::prepare() {
|
||||
setDimensions(st::colorEditWidth, height);
|
||||
|
||||
subscribe(_picker->changed(), [=] { updateFromControls(); });
|
||||
subscribe(_hueSlider->changed(), [=] { updateFromControls(); });
|
||||
subscribe(_opacitySlider->changed(), [=] { updateFromControls(); });
|
||||
if (_hueSlider) {
|
||||
subscribe(_hueSlider->changed(), [=] { updateFromControls(); });
|
||||
}
|
||||
if (_opacitySlider) {
|
||||
subscribe(_opacitySlider->changed(), [=] { updateFromControls(); });
|
||||
}
|
||||
if (_lightnessSlider) {
|
||||
subscribe(_lightnessSlider->changed(), [=] { updateFromControls(); });
|
||||
}
|
||||
|
||||
boxClosing() | rpl::start_with_next([=] {
|
||||
if (_cancelCallback) {
|
||||
@@ -671,7 +840,7 @@ void EditColorBox::prepare() {
|
||||
}, lifetime());
|
||||
|
||||
updateRGBFields();
|
||||
updateHSVFields();
|
||||
updateHSBFields();
|
||||
updateResultField();
|
||||
update();
|
||||
}
|
||||
@@ -704,21 +873,21 @@ void EditColorBox::fieldSubmitted() {
|
||||
}
|
||||
|
||||
void EditColorBox::saveColor() {
|
||||
_cancelCallback = Fn<void()>();
|
||||
const auto weak = make_weak(this);
|
||||
_cancelCallback = nullptr;
|
||||
if (_saveCallback) {
|
||||
_saveCallback(_new.toRgb());
|
||||
}
|
||||
closeBox();
|
||||
if (weak) {
|
||||
closeBox();
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::updateHSVFields() {
|
||||
auto hue = qRound((1. - _hueSlider->value()) * 360);
|
||||
auto saturation = qRound(_picker->valueX() * 255);
|
||||
auto brightness = qRound((1. - _picker->valueY()) * 255);
|
||||
auto alpha = qRound(_opacitySlider->value() * 255);
|
||||
_hueField->setTextWithFocus(QString::number(hue));
|
||||
_saturationField->setTextWithFocus(QString::number(percentFromByte(saturation)));
|
||||
_brightnessField->setTextWithFocus(QString::number(percentFromByte(brightness)));
|
||||
void EditColorBox::updateHSBFields() {
|
||||
const auto hsb = hsbFromControls();
|
||||
_hueField->setTextWithFocus(QString::number(hsb.hue));
|
||||
_saturationField->setTextWithFocus(QString::number(percentFromByte(hsb.saturation)));
|
||||
_brightnessField->setTextWithFocus(QString::number(percentFromByte(hsb.brightness)));
|
||||
}
|
||||
|
||||
void EditColorBox::updateRGBFields() {
|
||||
@@ -750,14 +919,28 @@ void EditColorBox::updateResultField() {
|
||||
}
|
||||
|
||||
void EditColorBox::resizeEvent(QResizeEvent *e) {
|
||||
auto fullwidth = _picker->width() + 2 * (st::colorEditSkip - st::colorSliderSkip) + _hueSlider->width() + st::colorSampleSize.width();
|
||||
const auto fullwidth = _picker->width()
|
||||
+ ((_mode == Mode::RGBA)
|
||||
? (2 * (st::colorEditSkip - st::colorSliderSkip) + _hueSlider->width())
|
||||
: (2 * st::colorEditSkip))
|
||||
+ st::colorSampleSize.width();
|
||||
auto left = (width() - fullwidth) / 2;
|
||||
_picker->moveToLeft(left, st::colorEditSkip);
|
||||
_hueSlider->setGeometryToLeft(_picker->x() + _picker->width() + st::colorEditSkip - st::colorSliderSkip, st::colorEditSkip - st::colorSliderSkip, _hueSlider->width(), st::colorPickerSize + 2 * st::colorSliderSkip);
|
||||
_opacitySlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _opacitySlider->height());
|
||||
auto fieldLeft = _hueSlider->x() + _hueSlider->width() - st::colorSliderSkip + st::colorEditSkip;
|
||||
auto fieldWidth = st::colorSampleSize.width();
|
||||
auto fieldHeight = _hueField->height();
|
||||
if (_hueSlider) {
|
||||
_hueSlider->setGeometryToLeft(_picker->x() + _picker->width() + st::colorEditSkip - st::colorSliderSkip, st::colorEditSkip - st::colorSliderSkip, _hueSlider->width(), st::colorPickerSize + 2 * st::colorSliderSkip);
|
||||
}
|
||||
if (_opacitySlider) {
|
||||
_opacitySlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _opacitySlider->height());
|
||||
}
|
||||
if (_lightnessSlider) {
|
||||
_lightnessSlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _lightnessSlider->height());
|
||||
}
|
||||
const auto fieldLeft = (_mode == Mode::RGBA)
|
||||
? (_hueSlider->x() + _hueSlider->width() + st::colorEditSkip - st::colorSliderSkip)
|
||||
: (_picker->x() + _picker->width() + st::colorEditSkip);
|
||||
const auto addWidth = (_mode == Mode::RGBA) ? 0 : st::colorEditSkip;
|
||||
const auto fieldWidth = st::colorSampleSize.width() + addWidth;
|
||||
const auto fieldHeight = _hueField->height();
|
||||
_newRect = QRect(fieldLeft, st::colorEditSkip, fieldWidth, st::colorSampleSize.height());
|
||||
_currentRect = _newRect.translated(0, st::colorSampleSize.height());
|
||||
_hueField->setGeometryToLeft(fieldLeft, _currentRect.y() + _currentRect.height() + st::colorFieldSkip, fieldWidth, fieldHeight);
|
||||
@@ -766,7 +949,13 @@ void EditColorBox::resizeEvent(QResizeEvent *e) {
|
||||
_redField->setGeometryToLeft(fieldLeft, _brightnessField->y() + _brightnessField->height() + st::colorFieldSkip, fieldWidth, fieldHeight);
|
||||
_greenField->setGeometryToLeft(fieldLeft, _redField->y() + _redField->height(), fieldWidth, fieldHeight);
|
||||
_blueField->setGeometryToLeft(fieldLeft, _greenField->y() + _greenField->height(), fieldWidth, fieldHeight);
|
||||
_result->setGeometryToLeft(fieldLeft - (st::colorEditSkip + st::colorSliderWidth), _opacitySlider->y() + _opacitySlider->height() - st::colorSliderSkip - _result->height(), fieldWidth + (st::colorEditSkip + st::colorSliderWidth), fieldHeight);
|
||||
const auto resultDelta = (_mode == Mode::RGBA)
|
||||
? (st::colorEditSkip + st::colorSliderWidth)
|
||||
: 0;
|
||||
const auto resultBottom = (_mode == Mode::RGBA)
|
||||
? (_opacitySlider->y() + _opacitySlider->height())
|
||||
: (_lightnessSlider->y() + _lightnessSlider->height());
|
||||
_result->setGeometryToLeft(fieldLeft - resultDelta, resultBottom - st::colorSliderSkip - _result->height(), fieldWidth + resultDelta, fieldHeight);
|
||||
}
|
||||
|
||||
void EditColorBox::paintEvent(QPaintEvent *e) {
|
||||
@@ -792,39 +981,74 @@ void EditColorBox::mousePressEvent(QMouseEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
EditColorBox::HSB EditColorBox::hsbFromControls() const {
|
||||
const auto hue = (_mode == Mode::RGBA)
|
||||
? qRound((1. - _hueSlider->value()) * 360)
|
||||
: qRound(_picker->valueX() * 360);
|
||||
const auto saturation = (_mode == Mode::RGBA)
|
||||
? qRound(_picker->valueX() * 255)
|
||||
: qRound((1. - _picker->valueY()) * 255);
|
||||
const auto brightness = (_mode == Mode::RGBA)
|
||||
? qRound((1. - _picker->valueY()) * 255)
|
||||
: (_lightnessMin
|
||||
+ qRound(_lightnessSlider->value()
|
||||
* (_lightnessMax - _lightnessMin)));
|
||||
return { hue, saturation, brightness };
|
||||
}
|
||||
|
||||
QColor EditColorBox::applyLimits(QColor color) const {
|
||||
if (_mode != Mode::HSL) {
|
||||
return color;
|
||||
}
|
||||
|
||||
const auto lightness = color.lightness();
|
||||
const auto clamped = std::clamp(lightness, _lightnessMin, _lightnessMax);
|
||||
if (clamped == lightness) {
|
||||
return color;
|
||||
}
|
||||
return QColor::fromHsl(color.hslHue(), color.hslSaturation(), clamped);
|
||||
}
|
||||
|
||||
void EditColorBox::updateFromColor(QColor color) {
|
||||
_new = color;
|
||||
_new = applyLimits(color);
|
||||
updateControlsFromColor();
|
||||
updateRGBFields();
|
||||
updateHSVFields();
|
||||
updateHSBFields();
|
||||
updateResultField();
|
||||
update();
|
||||
}
|
||||
|
||||
void EditColorBox::updateFromControls() {
|
||||
auto hue = qRound((1. - _hueSlider->value()) * 360);
|
||||
auto saturation = qRound(_picker->valueX() * 255);
|
||||
auto brightness = qRound((1. - _picker->valueY()) * 255);
|
||||
auto alpha = qRound(_opacitySlider->value() * 255);
|
||||
setHSV(hue, saturation, brightness, alpha);
|
||||
updateHSVFields();
|
||||
updateControlsFromHSV(hue, saturation, brightness);
|
||||
const auto hsb = hsbFromControls();
|
||||
const auto alpha = _opacitySlider
|
||||
? qRound(_opacitySlider->value() * 255)
|
||||
: 255;
|
||||
setHSB(hsb, alpha);
|
||||
updateHSBFields();
|
||||
updateControlsFromHSB(hsb);
|
||||
}
|
||||
|
||||
void EditColorBox::updateFromHSVFields() {
|
||||
auto hue = _hueField->value();
|
||||
auto saturation = percentToByte(_saturationField->value());
|
||||
auto brightness = percentToByte(_brightnessField->value());
|
||||
auto alpha = qRound(_opacitySlider->value() * 255);
|
||||
setHSV(hue, saturation, brightness, alpha);
|
||||
updateControlsFromHSV(hue, saturation, brightness);
|
||||
void EditColorBox::updateFromHSBFields() {
|
||||
const auto hue = _hueField->value();
|
||||
const auto saturation = percentToByte(_saturationField->value());
|
||||
const auto brightness = std::clamp(
|
||||
percentToByte(_brightnessField->value()),
|
||||
_lightnessMin,
|
||||
_lightnessMax);
|
||||
const auto alpha = _opacitySlider
|
||||
? qRound(_opacitySlider->value() * 255)
|
||||
: 255;
|
||||
setHSB({ hue, saturation, brightness }, alpha);
|
||||
updateControlsFromHSB({ hue, saturation, brightness });
|
||||
}
|
||||
|
||||
void EditColorBox::updateFromRGBFields() {
|
||||
auto red = _redField->value();
|
||||
auto blue = _blueField->value();
|
||||
auto green = _greenField->value();
|
||||
auto alpha = qRound(_opacitySlider->value() * 255);
|
||||
const auto red = _redField->value();
|
||||
const auto blue = _blueField->value();
|
||||
const auto green = _greenField->value();
|
||||
const auto alpha = _opacitySlider
|
||||
? qRound(_opacitySlider->value() * 255)
|
||||
: 255;
|
||||
setRGB(red, green, blue, alpha);
|
||||
updateResultField();
|
||||
}
|
||||
@@ -855,10 +1079,17 @@ void EditColorBox::updateFromResultField() {
|
||||
updateRGBFields();
|
||||
}
|
||||
|
||||
void EditColorBox::updateControlsFromHSV(int hue, int saturation, int brightness) {
|
||||
_picker->setHSV(hue, saturation, brightness);
|
||||
_hueSlider->setHSV(hue, saturation, brightness);
|
||||
_opacitySlider->setHSV(hue, saturation, brightness);
|
||||
void EditColorBox::updateControlsFromHSB(HSB hsb) {
|
||||
_picker->setHSB(hsb);
|
||||
if (_hueSlider) {
|
||||
_hueSlider->setHSB(hsb);
|
||||
}
|
||||
if (_opacitySlider) {
|
||||
_opacitySlider->setHSB(hsb);
|
||||
}
|
||||
if (_lightnessSlider) {
|
||||
_lightnessSlider->setHSB(hsb);
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::updateControlsFromColor() {
|
||||
@@ -867,21 +1098,32 @@ void EditColorBox::updateControlsFromColor() {
|
||||
auto blue = _new.blue();
|
||||
auto alpha = _new.alpha();
|
||||
_picker->setRGB(red, green, blue);
|
||||
_hueSlider->setRGB(red, green, blue);
|
||||
_opacitySlider->setRGB(red, green, blue);
|
||||
_opacitySlider->setAlpha(alpha);
|
||||
if (_hueSlider) {
|
||||
_hueSlider->setRGB(red, green, blue);
|
||||
}
|
||||
if (_opacitySlider) {
|
||||
_opacitySlider->setRGB(red, green, blue);
|
||||
_opacitySlider->setAlpha(alpha);
|
||||
}
|
||||
if (_lightnessSlider) {
|
||||
_lightnessSlider->setRGB(red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::setHSV(int hue, int saturation, int value, int alpha) {
|
||||
_new.setHsv(hue, saturation, value, alpha);
|
||||
void EditColorBox::setHSB(HSB hsb, int alpha) {
|
||||
if (_mode == Mode::RGBA) {
|
||||
_new.setHsv(hsb.hue, hsb.saturation, hsb.brightness, alpha);
|
||||
} else {
|
||||
_new.setHsl(hsb.hue, hsb.saturation, hsb.brightness, alpha);
|
||||
}
|
||||
updateRGBFields();
|
||||
updateResultField();
|
||||
update();
|
||||
}
|
||||
|
||||
void EditColorBox::setRGB(int red, int green, int blue, int alpha) {
|
||||
_new.setRgb(red, green, blue, alpha);
|
||||
_new = applyLimits(QColor(red, green, blue, alpha));
|
||||
updateControlsFromColor();
|
||||
updateHSVFields();
|
||||
updateHSBFields();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class EditColorBox : public BoxContent {
|
||||
public:
|
||||
EditColorBox(QWidget*, const QString &title, QColor current = QColor(255, 255, 255));
|
||||
enum class Mode {
|
||||
RGBA,
|
||||
HSL,
|
||||
};
|
||||
EditColorBox(QWidget*, const QString &title, Mode mode, QColor current);
|
||||
|
||||
void setLightnessLimits(int min, int max);
|
||||
|
||||
void setSaveCallback(Fn<void(QColor)> callback) {
|
||||
_saveCallback = std::move(callback);
|
||||
@@ -36,21 +42,28 @@ protected:
|
||||
void setInnerFocus() override;
|
||||
|
||||
private:
|
||||
struct HSB { // HSV or HSL depending on Mode.
|
||||
int hue = 0;
|
||||
int saturation = 0;
|
||||
int brightness = 0;
|
||||
};
|
||||
void saveColor();
|
||||
void fieldSubmitted();
|
||||
|
||||
[[nodiscard]] HSB hsbFromControls() const;
|
||||
void updateFromColor(QColor color);
|
||||
void updateControlsFromColor();
|
||||
void updateControlsFromHSV(int hue, int saturation, int brightness);
|
||||
void updateHSVFields();
|
||||
void updateControlsFromHSB(HSB hsb);
|
||||
void updateHSBFields();
|
||||
void updateRGBFields();
|
||||
void updateResultField();
|
||||
void updateFromControls();
|
||||
void updateFromHSVFields();
|
||||
void updateFromHSBFields();
|
||||
void updateFromRGBFields();
|
||||
void updateFromResultField();
|
||||
void setHSV(int hue, int saturation, int brightness, int alpha);
|
||||
void setHSB(HSB hsb, int alpha);
|
||||
void setRGB(int red, int green, int blue, int alpha);
|
||||
[[nodiscard]] QColor applyLimits(QColor color) const;
|
||||
|
||||
int percentFromByte(int byte) {
|
||||
return snap(qRound(byte * 100 / 255.), 0, 100);
|
||||
@@ -65,10 +78,12 @@ private:
|
||||
class ResultField;
|
||||
|
||||
QString _title;
|
||||
Mode _mode = Mode();
|
||||
|
||||
object_ptr<Picker> _picker;
|
||||
object_ptr<Slider> _hueSlider;
|
||||
object_ptr<Slider> _opacitySlider;
|
||||
object_ptr<Slider> _hueSlider = { nullptr };
|
||||
object_ptr<Slider> _opacitySlider = { nullptr };
|
||||
object_ptr<Slider> _lightnessSlider = { nullptr };
|
||||
|
||||
object_ptr<Field> _hueField;
|
||||
object_ptr<Field> _saturationField;
|
||||
@@ -85,6 +100,9 @@ private:
|
||||
QRect _currentRect;
|
||||
QRect _newRect;
|
||||
|
||||
int _lightnessMin = 0;
|
||||
int _lightnessMax = 255;
|
||||
|
||||
Fn<void(QColor)> _saveCallback;
|
||||
Fn<void()> _cancelCallback;
|
||||
|
||||
|
||||
@@ -250,8 +250,9 @@ bool EditPrivacyBox::showExceptionLink(Exception exception) const {
|
||||
Unexpected("Invalid exception value.");
|
||||
}
|
||||
|
||||
Ui::Radioenum<EditPrivacyBox::Option> *EditPrivacyBox::addOption(
|
||||
Ui::Radioenum<EditPrivacyBox::Option> *EditPrivacyBox::AddOption(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<EditPrivacyController*> controller,
|
||||
const std::shared_ptr<Ui::RadioenumGroup<Option>> &group,
|
||||
Option option) {
|
||||
return container->add(
|
||||
@@ -259,7 +260,7 @@ Ui::Radioenum<EditPrivacyBox::Option> *EditPrivacyBox::addOption(
|
||||
container,
|
||||
group,
|
||||
option,
|
||||
_controller->optionLabel(option),
|
||||
controller->optionLabel(option),
|
||||
st::settingsSendType),
|
||||
st::settingsSendTypePadding);
|
||||
}
|
||||
@@ -306,7 +307,7 @@ void EditPrivacyBox::setupContent() {
|
||||
|
||||
const auto addOptionRow = [&](Option option) {
|
||||
return (_controller->hasOption(option) || (_value.option == option))
|
||||
? addOption(content, group, option)
|
||||
? AddOption(content, _controller.get(), group, option)
|
||||
: nullptr;
|
||||
};
|
||||
const auto addExceptionLink = [=](Exception exception) {
|
||||
@@ -345,7 +346,7 @@ void EditPrivacyBox::setupContent() {
|
||||
|
||||
auto above = _controller->setupAboveWidget(
|
||||
content,
|
||||
std::move(optionValue));
|
||||
rpl::duplicate(optionValue));
|
||||
if (above) {
|
||||
content->add(std::move(above));
|
||||
}
|
||||
@@ -357,6 +358,14 @@ void EditPrivacyBox::setupContent() {
|
||||
addLabel(content, _controller->warning());
|
||||
AddSkip(content);
|
||||
|
||||
auto middle = _controller->setupMiddleWidget(
|
||||
_window,
|
||||
content,
|
||||
std::move(optionValue));
|
||||
if (middle) {
|
||||
content->add(std::move(middle));
|
||||
}
|
||||
|
||||
AddDivider(content);
|
||||
AddSkip(content);
|
||||
AddSubsectionTitle(content, tr::lng_edit_privacy_exceptions());
|
||||
@@ -373,6 +382,7 @@ void EditPrivacyBox::setupContent() {
|
||||
const auto someAreDisallowed = (_value.option != Option::Everyone)
|
||||
|| !_value.never.empty();
|
||||
_controller->confirmSave(someAreDisallowed, crl::guard(this, [=] {
|
||||
_controller->saveAdditional();
|
||||
_window->session().api().savePrivacy(
|
||||
_controller->apiKey(),
|
||||
collectResult());
|
||||
|
||||
@@ -58,6 +58,12 @@ public:
|
||||
rpl::producer<Option> option) {
|
||||
return { nullptr };
|
||||
}
|
||||
[[nodiscard]] virtual object_ptr<Ui::RpWidget> setupMiddleWidget(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<QWidget*> parent,
|
||||
rpl::producer<Option> option) {
|
||||
return { nullptr };
|
||||
}
|
||||
[[nodiscard]] virtual object_ptr<Ui::RpWidget> setupBelowWidget(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<QWidget*> parent) {
|
||||
@@ -69,6 +75,8 @@ public:
|
||||
FnMut<void()> saveCallback) {
|
||||
saveCallback();
|
||||
}
|
||||
virtual void saveAdditional() {
|
||||
}
|
||||
|
||||
virtual ~EditPrivacyController() = default;
|
||||
|
||||
@@ -100,6 +108,12 @@ public:
|
||||
std::unique_ptr<EditPrivacyController> controller,
|
||||
const Value &value);
|
||||
|
||||
static Ui::Radioenum<Option> *AddOption(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<EditPrivacyController*> controller,
|
||||
const std::shared_ptr<Ui::RadioenumGroup<Option>> &group,
|
||||
Option option);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
@@ -108,10 +122,6 @@ private:
|
||||
void setupContent();
|
||||
QVector<MTPInputPrivacyRule> collectResult();
|
||||
|
||||
Ui::Radioenum<Option> *addOption(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
const std::shared_ptr<Ui::RadioenumGroup<Option>> &group,
|
||||
Option option);
|
||||
Ui::FlatLabel *addLabel(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<QString> text);
|
||||
|
||||
@@ -129,7 +129,7 @@ template <std::size_t... I>
|
||||
inline void GenericBox::Initer<InitMethod, InitArgs...>::call(
|
||||
not_null<GenericBox*> box,
|
||||
std::index_sequence<I...>) {
|
||||
std::invoke(method, box, std::get<I>(args)...);
|
||||
std::invoke(method, box, std::get<I>(std::move(args))...);
|
||||
}
|
||||
|
||||
template <typename InitMethod, typename ...InitArgs>
|
||||
|
||||
@@ -32,6 +32,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_passport.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace {
|
||||
|
||||
using Language = Lang::Language;
|
||||
@@ -408,7 +411,7 @@ bool Rows::hasMenu(not_null<const Row*> row) const {
|
||||
|
||||
void Rows::share(not_null<const Row*> row) const {
|
||||
const auto link = qsl("https://t.me/setlanguage/") + row->data.id;
|
||||
QApplication::clipboard()->setText(link);
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
Ui::Toast::Show(tr::lng_username_copied(tr::now));
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) {
|
||||
MTP_string(),
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
MTPVector<MTPMessageEntity>()
|
||||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(0) // schedule_date
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result, randomId);
|
||||
}).fail([=](const RPCError &error) {
|
||||
|
||||
@@ -299,7 +299,7 @@ void AddSpecialBoxController::migrate(not_null<ChannelData*> channel) {
|
||||
|
||||
std::unique_ptr<PeerListRow> AddSpecialBoxController::createSearchRow(
|
||||
not_null<PeerData*> peer) {
|
||||
if (peer->isSelf()) {
|
||||
if (_excludeSelf && peer->isSelf()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (const auto user = peer->asUser()) {
|
||||
@@ -312,6 +312,8 @@ void AddSpecialBoxController::prepare() {
|
||||
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
|
||||
auto title = [&] {
|
||||
switch (_role) {
|
||||
case Role::Members:
|
||||
return tr::lng_profile_participants_section();
|
||||
case Role::Admins:
|
||||
return tr::lng_channel_add_admin();
|
||||
case Role::Restricted:
|
||||
@@ -799,7 +801,8 @@ void AddSpecialBoxController::kickUser(
|
||||
}
|
||||
|
||||
bool AddSpecialBoxController::appendRow(not_null<UserData*> user) {
|
||||
if (delegate()->peerListFindRow(user->id) || user->isSelf()) {
|
||||
if (delegate()->peerListFindRow(user->id)
|
||||
|| (_excludeSelf && user->isSelf())) {
|
||||
return false;
|
||||
}
|
||||
delegate()->peerListAppendRow(createRow(user));
|
||||
|
||||
@@ -131,6 +131,9 @@ private:
|
||||
AdminDoneCallback _adminDoneCallback;
|
||||
BannedDoneCallback _bannedDoneCallback;
|
||||
|
||||
protected:
|
||||
bool _excludeSelf = true;
|
||||
|
||||
};
|
||||
|
||||
// Finds chat/channel members, then contacts, then global search results.
|
||||
|
||||
@@ -25,6 +25,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxUserFirstLastName = 64; // See also add_contact_box.
|
||||
|
||||
QString UserPhone(not_null<UserData*> user) {
|
||||
const auto phone = user->phone();
|
||||
return phone.isEmpty()
|
||||
@@ -220,6 +222,8 @@ void Controller::initNameFields(
|
||||
};
|
||||
QObject::connect(first, &Ui::InputField::submitted, submit);
|
||||
QObject::connect(last, &Ui::InputField::submitted, submit);
|
||||
first->setMaxLength(kMaxUserFirstLastName);
|
||||
first->setMaxLength(kMaxUserFirstLastName);
|
||||
}
|
||||
|
||||
void Controller::setupWarning() {
|
||||
|
||||
@@ -354,6 +354,18 @@ void EditAdminBox::prepare() {
|
||||
) | rpl::then(std::move(
|
||||
changes
|
||||
));
|
||||
_aboutAddAdmins = addControl(
|
||||
object_ptr<Ui::FlatLabel>(this, st::boxDividerLabel),
|
||||
st::rightsAboutMargin);
|
||||
rpl::duplicate(
|
||||
selectedFlags
|
||||
) | rpl::map(
|
||||
(_1 & Flag::f_add_admins) != 0
|
||||
) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](bool checked) {
|
||||
refreshAboutAddAdminsText(checked);
|
||||
}, lifetime());
|
||||
|
||||
if (canTransferOwnership()) {
|
||||
const auto allFlags = FullAdminRights(isGroup);
|
||||
setupTransferButton(
|
||||
@@ -364,17 +376,6 @@ void EditAdminBox::prepare() {
|
||||
((_1 & allFlags) == allFlags)
|
||||
))->setDuration(0);
|
||||
}
|
||||
_aboutAddAdmins = addControl(
|
||||
object_ptr<Ui::FlatLabel>(this, st::boxDividerLabel),
|
||||
st::rightsAboutMargin);
|
||||
std::move(
|
||||
selectedFlags
|
||||
) | rpl::map(
|
||||
(_1 & Flag::f_add_admins) != 0
|
||||
) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](bool checked) {
|
||||
refreshAboutAddAdminsText(checked);
|
||||
}, lifetime());
|
||||
|
||||
if (canSave()) {
|
||||
const auto rank = (chat || channel->isMegagroup())
|
||||
@@ -470,13 +471,10 @@ not_null<Ui::SlideWrap<Ui::RpWidget>*> EditAdminBox::setupTransferButton(
|
||||
object_ptr<Ui::VerticalLayout>(this)));
|
||||
|
||||
const auto container = wrap->entity();
|
||||
const auto addDivider = [&] {
|
||||
container->add(
|
||||
object_ptr<BoxContentDivider>(container),
|
||||
{ 0, st::infoProfileSkip, 0, st::infoProfileSkip });
|
||||
};
|
||||
|
||||
addDivider();
|
||||
container->add(
|
||||
object_ptr<BoxContentDivider>(container),
|
||||
{ 0, st::infoProfileSkip, 0, st::infoProfileSkip });
|
||||
container->add(EditPeerInfoBox::CreateButton(
|
||||
this,
|
||||
(isGroup
|
||||
@@ -485,7 +483,6 @@ not_null<Ui::SlideWrap<Ui::RpWidget>*> EditAdminBox::setupTransferButton(
|
||||
rpl::single(QString()),
|
||||
[=] { transferOwnership(); },
|
||||
st::peerPermissionsButton));
|
||||
addDivider();
|
||||
|
||||
return wrap;
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ bool ParticipantsAdditionalData::canAddOrEditAdmin(
|
||||
|
||||
bool ParticipantsAdditionalData::canRestrictUser(
|
||||
not_null<UserData*> user) const {
|
||||
if (!canEditAdmin(user)) {
|
||||
if (!canEditAdmin(user) || user->isSelf()) {
|
||||
return false;
|
||||
} else if (const auto chat = _peer->asChat()) {
|
||||
return chat->canBanMembers();
|
||||
|
||||
@@ -235,7 +235,7 @@ void ShowEditPermissions(not_null<PeerData*> peer) {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxGroupChannelTitle = 255; // See also add_contact_box.
|
||||
constexpr auto kMaxGroupChannelTitle = 128; // See also add_contact_box.
|
||||
constexpr auto kMaxChannelDescription = 255; // See also add_contact_box.
|
||||
|
||||
class Controller
|
||||
|
||||
@@ -39,6 +39,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_session_controller.h"
|
||||
#include <rpl/flatten_latest.h>
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kUsernameCheckTimeout = crl::time(200);
|
||||
@@ -607,7 +610,7 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() {
|
||||
_controls.inviteLink->setContextCopyText(QString());
|
||||
_controls.inviteLink->setBreakEverywhere(true);
|
||||
_controls.inviteLink->setClickHandlerFilter([=](auto&&...) {
|
||||
QApplication::clipboard()->setText(inviteLinkText());
|
||||
QGuiApplication::clipboard()->setText(inviteLinkText());
|
||||
Ui::Toast::Show(tr::lng_group_invite_copied(tr::now));
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "chat_helpers/tabbed_panel.h"
|
||||
#include "chat_helpers/tabbed_selector.h"
|
||||
#include "history/view/history_view_schedule_box.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/mime_type.h"
|
||||
#include "core/event_filter.h"
|
||||
@@ -30,12 +31,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lottie/lottie_single_player.h"
|
||||
#include "data/data_document.h"
|
||||
#include "media/clip/media_clip_reader.h"
|
||||
#include "api/api_common.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "layout.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
#include <QtCore/QMimeData>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMinPreviewWidth = 20;
|
||||
@@ -1366,12 +1370,16 @@ SendFilesBox::SendFilesBox(
|
||||
Storage::PreparedList &&list,
|
||||
const TextWithTags &caption,
|
||||
CompressConfirm compressed,
|
||||
SendLimit limit)
|
||||
SendLimit limit,
|
||||
Api::SendType sendType,
|
||||
SendMenuType sendMenuType)
|
||||
: _controller(controller)
|
||||
, _sendType(sendType)
|
||||
, _list(std::move(list))
|
||||
, _compressConfirmInitial(compressed)
|
||||
, _compressConfirm(compressed)
|
||||
, _sendLimit(limit)
|
||||
, _sendMenuType(sendMenuType)
|
||||
, _caption(
|
||||
this,
|
||||
st::confirmCaptionArea,
|
||||
@@ -1468,8 +1476,14 @@ void SendFilesBox::setupShadows(
|
||||
}
|
||||
|
||||
void SendFilesBox::prepare() {
|
||||
_send = addButton(tr::lng_send_button(), [=] { send(); });
|
||||
SetupSendWithoutSound(_send, [=] { return true; }, [=] { send(true); });
|
||||
_send = addButton(tr::lng_send_button(), [=] { send({}); });
|
||||
if (_sendType == Api::SendType::Normal) {
|
||||
SetupSendMenu(
|
||||
_send,
|
||||
[=] { return _sendMenuType; },
|
||||
[=] { sendSilent(); },
|
||||
[=] { sendScheduled(); });
|
||||
}
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
initSendWay();
|
||||
setupCaption();
|
||||
@@ -1638,7 +1652,7 @@ void SendFilesBox::setupCaption() {
|
||||
const auto ctrlShiftEnter = modifiers.testFlag(Qt::ShiftModifier)
|
||||
&& (modifiers.testFlag(Qt::ControlModifier)
|
||||
|| modifiers.testFlag(Qt::MetaModifier));
|
||||
send(false, ctrlShiftEnter);
|
||||
send({}, ctrlShiftEnter);
|
||||
});
|
||||
connect(_caption, &Ui::InputField::cancelled, [=] { closeBox(); });
|
||||
_caption->setMimeDataHook([=](
|
||||
@@ -1682,14 +1696,16 @@ void SendFilesBox::setupEmojiPanel() {
|
||||
st::emojiPanMinHeight / 2,
|
||||
st::emojiPanMinHeight);
|
||||
_emojiPanel->hide();
|
||||
_emojiPanel->getSelector()->emojiChosen(
|
||||
_emojiPanel->selector()->emojiChosen(
|
||||
) | rpl::start_with_next([=](EmojiPtr emoji) {
|
||||
Ui::InsertEmojiAtCursor(_caption->textCursor(), emoji);
|
||||
}, lifetime());
|
||||
|
||||
_emojiFilter.reset(Core::InstallEventFilter(
|
||||
container,
|
||||
[=](not_null<QEvent*> event) { return emojiFilter(event); }));
|
||||
const auto filterCallback = [=](not_null<QEvent*> event) {
|
||||
emojiFilterForGeometry(event);
|
||||
return Core::EventFilter::Result::Continue;
|
||||
};
|
||||
_emojiFilter.reset(Core::InstallEventFilter(container, filterCallback));
|
||||
|
||||
_emojiToggle.create(this, st::boxAttachEmoji);
|
||||
_emojiToggle->setVisible(!_caption->isHidden());
|
||||
@@ -1699,14 +1715,13 @@ void SendFilesBox::setupEmojiPanel() {
|
||||
});
|
||||
}
|
||||
|
||||
bool SendFilesBox::emojiFilter(not_null<QEvent*> event) {
|
||||
void SendFilesBox::emojiFilterForGeometry(not_null<QEvent*> event) {
|
||||
const auto type = event->type();
|
||||
if (type == QEvent::Move || type == QEvent::Resize) {
|
||||
// updateEmojiPanelGeometry uses not only container geometry, but
|
||||
// also container children geometries that will be updated later.
|
||||
crl::on_main(this, [=] { updateEmojiPanelGeometry(); });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SendFilesBox::updateEmojiPanelGeometry() {
|
||||
@@ -1842,7 +1857,7 @@ void SendFilesBox::keyPressEvent(QKeyEvent *e) {
|
||||
const auto ctrl = modifiers.testFlag(Qt::ControlModifier)
|
||||
|| modifiers.testFlag(Qt::MetaModifier);
|
||||
const auto shift = modifiers.testFlag(Qt::ShiftModifier);
|
||||
send(false, ctrl && shift);
|
||||
send({}, ctrl && shift);
|
||||
} else {
|
||||
BoxContent::keyPressEvent(e);
|
||||
}
|
||||
@@ -1913,7 +1928,13 @@ void SendFilesBox::setInnerFocus() {
|
||||
}
|
||||
}
|
||||
|
||||
void SendFilesBox::send(bool silent, bool ctrlShiftEnter) {
|
||||
void SendFilesBox::send(
|
||||
Api::SendOptions options,
|
||||
bool ctrlShiftEnter) {
|
||||
if (_sendType == Api::SendType::Scheduled && !options.scheduled) {
|
||||
return sendScheduled();
|
||||
}
|
||||
|
||||
using Way = SendFilesWay;
|
||||
const auto way = _sendWay ? _sendWay->value() : Way::Files;
|
||||
|
||||
@@ -1942,10 +1963,23 @@ void SendFilesBox::send(bool silent, bool ctrlShiftEnter) {
|
||||
std::move(_list),
|
||||
way,
|
||||
std::move(caption),
|
||||
silent,
|
||||
options,
|
||||
ctrlShiftEnter);
|
||||
}
|
||||
closeBox();
|
||||
}
|
||||
|
||||
void SendFilesBox::sendSilent() {
|
||||
auto options = Api::SendOptions();
|
||||
options.silent = true;
|
||||
send(options);
|
||||
}
|
||||
|
||||
void SendFilesBox::sendScheduled() {
|
||||
const auto callback = [=](Api::SendOptions options) { send(options); };
|
||||
Ui::show(
|
||||
HistoryView::PrepareScheduleBox(this, _sendMenuType, callback),
|
||||
LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
SendFilesBox::~SendFilesBox() = default;
|
||||
|
||||
@@ -16,6 +16,11 @@ namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace Api {
|
||||
struct SendOptions;
|
||||
enum class SendType;
|
||||
} // namespace Api
|
||||
|
||||
namespace ChatHelpers {
|
||||
class TabbedPanel;
|
||||
} // namespace ChatHelpers
|
||||
@@ -35,6 +40,8 @@ namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
enum class SendMenuType;
|
||||
|
||||
enum class SendFilesWay {
|
||||
Album,
|
||||
Photos,
|
||||
@@ -53,14 +60,16 @@ public:
|
||||
Storage::PreparedList &&list,
|
||||
const TextWithTags &caption,
|
||||
CompressConfirm compressed,
|
||||
SendLimit limit);
|
||||
SendLimit limit,
|
||||
Api::SendType sendType,
|
||||
SendMenuType sendMenuType);
|
||||
|
||||
void setConfirmedCallback(
|
||||
Fn<void(
|
||||
Storage::PreparedList &&list,
|
||||
SendFilesWay way,
|
||||
TextWithTags &&caption,
|
||||
bool silent,
|
||||
Api::SendOptions options,
|
||||
bool ctrlShiftEnter)> callback) {
|
||||
_confirmedCallback = std::move(callback);
|
||||
}
|
||||
@@ -93,7 +102,7 @@ private:
|
||||
|
||||
void setupEmojiPanel();
|
||||
void updateEmojiPanelGeometry();
|
||||
bool emojiFilter(not_null<QEvent*> event);
|
||||
void emojiFilterForGeometry(not_null<QEvent*> event);
|
||||
|
||||
void refreshAlbumMediaCount();
|
||||
void preparePreview();
|
||||
@@ -101,7 +110,9 @@ private:
|
||||
void prepareAlbumPreview();
|
||||
void applyAlbumOrder();
|
||||
|
||||
void send(bool silent = false, bool ctrlShiftEnter = false);
|
||||
void send(Api::SendOptions options, bool ctrlShiftEnter = false);
|
||||
void sendSilent();
|
||||
void sendScheduled();
|
||||
void captionResized();
|
||||
|
||||
void setupTitleText();
|
||||
@@ -113,7 +124,8 @@ private:
|
||||
bool canAddUrls(const QList<QUrl> &urls) const;
|
||||
bool addFiles(not_null<const QMimeData*> data);
|
||||
|
||||
not_null<Window::SessionController*> _controller;
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
const Api::SendType _sendType = Api::SendType();
|
||||
|
||||
QString _titleText;
|
||||
int _titleHeight = 0;
|
||||
@@ -123,12 +135,13 @@ private:
|
||||
CompressConfirm _compressConfirmInitial = CompressConfirm::None;
|
||||
CompressConfirm _compressConfirm = CompressConfirm::None;
|
||||
SendLimit _sendLimit = SendLimit::Many;
|
||||
SendMenuType _sendMenuType = SendMenuType();
|
||||
|
||||
Fn<void(
|
||||
Storage::PreparedList &&list,
|
||||
SendFilesWay way,
|
||||
TextWithTags &&caption,
|
||||
bool silent,
|
||||
Api::SendOptions options,
|
||||
bool ctrlShiftEnter)> _confirmedCallback;
|
||||
Fn<void()> _cancelledCallback;
|
||||
bool _confirmed = false;
|
||||
|
||||
@@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/view/history_view_schedule_box.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
@@ -195,7 +196,7 @@ void ShareBox::prepareCommentField() {
|
||||
const auto field = _comment->entity();
|
||||
|
||||
connect(field, &Ui::InputField::submitted, [=] {
|
||||
submit();
|
||||
submit({});
|
||||
});
|
||||
|
||||
field->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
@@ -242,7 +243,7 @@ void ShareBox::prepare() {
|
||||
_select->setSubmittedCallback([=](Qt::KeyboardModifiers modifiers) {
|
||||
if (modifiers.testFlag(Qt::ControlModifier)
|
||||
|| modifiers.testFlag(Qt::MetaModifier)) {
|
||||
submit();
|
||||
submit({});
|
||||
} else {
|
||||
_inner->selectActive();
|
||||
}
|
||||
@@ -408,16 +409,24 @@ void ShareBox::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
SendMenuType ShareBox::sendMenuType() const {
|
||||
const auto selected = _inner->selected();
|
||||
return (selected.size() == 1 && selected.front()->isSelf())
|
||||
? SendMenuType::Reminder
|
||||
: SendMenuType::Scheduled;
|
||||
}
|
||||
|
||||
void ShareBox::createButtons() {
|
||||
clearButtons();
|
||||
if (_hasSelected) {
|
||||
const auto send = addButton(tr::lng_share_confirm(), [=] {
|
||||
submit();
|
||||
submit({});
|
||||
});
|
||||
SetupSendWithoutSound(
|
||||
SetupSendMenu(
|
||||
send,
|
||||
[=] { return true; },
|
||||
[=] { submit(true); });
|
||||
[=] { return sendMenuType(); },
|
||||
[=] { submitSilent(); },
|
||||
[=] { submitScheduled(); });
|
||||
} else if (_copyCallback) {
|
||||
addButton(tr::lng_share_copy_link(), [=] { copyLink(); });
|
||||
}
|
||||
@@ -451,15 +460,28 @@ void ShareBox::innerSelectedChanged(PeerData *peer, bool checked) {
|
||||
update();
|
||||
}
|
||||
|
||||
void ShareBox::submit(bool silent) {
|
||||
void ShareBox::submit(Api::SendOptions options) {
|
||||
if (_submitCallback) {
|
||||
_submitCallback(
|
||||
_inner->selected(),
|
||||
_comment->entity()->getTextWithAppliedMarkdown(),
|
||||
silent);
|
||||
options);
|
||||
}
|
||||
}
|
||||
|
||||
void ShareBox::submitSilent() {
|
||||
auto options = Api::SendOptions();
|
||||
options.silent = true;
|
||||
submit(options);
|
||||
}
|
||||
|
||||
void ShareBox::submitScheduled() {
|
||||
const auto callback = [=](Api::SendOptions options) { submit(options); };
|
||||
Ui::show(
|
||||
HistoryView::PrepareScheduleBox(this, sendMenuType(), callback),
|
||||
LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
void ShareBox::copyLink() {
|
||||
if (_copyCallback) {
|
||||
_copyCallback();
|
||||
|
||||
@@ -13,10 +13,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/effects/round_checkbox.h"
|
||||
|
||||
enum class SendMenuType;
|
||||
|
||||
namespace Window {
|
||||
class SessionNavigation;
|
||||
} // namespace Window
|
||||
|
||||
namespace Api {
|
||||
struct SendOptions;
|
||||
} // namespace Api
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@@ -52,7 +58,7 @@ public:
|
||||
using SubmitCallback = Fn<void(
|
||||
QVector<PeerData*>&&,
|
||||
TextWithTags&&,
|
||||
bool)>;
|
||||
Api::SendOptions)>;
|
||||
using FilterCallback = Fn<bool(PeerData*)>;
|
||||
|
||||
ShareBox(
|
||||
@@ -73,10 +79,14 @@ private:
|
||||
void prepareCommentField();
|
||||
void scrollAnimationCallback();
|
||||
|
||||
void submit(bool silent = false);
|
||||
void submit(Api::SendOptions options);
|
||||
void submitSilent();
|
||||
void submitScheduled();
|
||||
void copyLink();
|
||||
bool searchByUsername(bool useCache = false);
|
||||
|
||||
SendMenuType sendMenuType() const;
|
||||
|
||||
void scrollTo(Ui::ScrollToRequest request);
|
||||
void needSearchByUsername();
|
||||
void applyFilterUpdate(const QString &query);
|
||||
|
||||
@@ -32,6 +32,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kStickersPanelPerRow = 5;
|
||||
@@ -132,16 +135,17 @@ StickerSetBox::StickerSetBox(
|
||||
, _set(set) {
|
||||
}
|
||||
|
||||
void StickerSetBox::Show(
|
||||
QPointer<BoxContent> StickerSetBox::Show(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<DocumentData*> document) {
|
||||
if (const auto sticker = document->sticker()) {
|
||||
if (sticker->set.type() != mtpc_inputStickerSetEmpty) {
|
||||
Ui::show(
|
||||
return Ui::show(
|
||||
Box<StickerSetBox>(controller, sticker->set),
|
||||
LayerOption::KeepOther);
|
||||
LayerOption::KeepOther).data();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void StickerSetBox::prepare() {
|
||||
@@ -177,7 +181,7 @@ void StickerSetBox::addStickers() {
|
||||
|
||||
void StickerSetBox::shareStickers() {
|
||||
auto url = Core::App().createInternalLinkFull(qsl("addstickers/") + _inner->shortName());
|
||||
QApplication::clipboard()->setText(url);
|
||||
QGuiApplication::clipboard()->setText(url);
|
||||
Ui::show(Box<InformBox>(tr::lng_stickers_copied(tr::now)));
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
not_null<Window::SessionController*> controller,
|
||||
const MTPInputStickerSet &set);
|
||||
|
||||
static void Show(
|
||||
static QPointer<BoxContent> Show(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<DocumentData*> document);
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ not_null<Ui::RpWidget*> UrlAuthBox::setupContent(
|
||||
st::boxPadding.bottom(),
|
||||
st::boxPadding.right(),
|
||||
st::boxPadding.bottom()));
|
||||
checkbox->setAllowMultiline(true);
|
||||
checkbox->setAllowTextLines();
|
||||
checkbox->setText(text, true);
|
||||
return checkbox;
|
||||
};
|
||||
|
||||
@@ -19,6 +19,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_user.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMinUsernameLength = 5;
|
||||
@@ -167,7 +170,7 @@ void UsernameBox::changed() {
|
||||
}
|
||||
|
||||
void UsernameBox::linkClick() {
|
||||
QApplication::clipboard()->setText(Core::App().createInternalLinkFull(getName()));
|
||||
QGuiApplication::clipboard()->setText(Core::App().createInternalLinkFull(getName()));
|
||||
Ui::Toast::Show(tr::lng_username_copied(tr::now));
|
||||
}
|
||||
|
||||
|
||||
@@ -324,6 +324,7 @@ void BoxController::receivedCalls(const QVector<MTPMessage> &result) {
|
||||
if (const auto peer = session().data().peerLoaded(peerId)) {
|
||||
const auto item = session().data().addNewMessage(
|
||||
message,
|
||||
MTPDmessage_ClientFlags(),
|
||||
NewMessageType::Existing);
|
||||
insertRow(item, InsertWay::Append);
|
||||
} else {
|
||||
|
||||
@@ -31,6 +31,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/main_window.h"
|
||||
#include "layout.h"
|
||||
|
||||
#include <QtWidgets/QDesktopWidget>
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
namespace Calls {
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace ChatHelpers {
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtGui/QTextBlock>
|
||||
|
||||
namespace Ui {
|
||||
namespace Emoji {
|
||||
namespace {
|
||||
@@ -518,12 +521,20 @@ SuggestionsController::SuggestionsController(
|
||||
|
||||
setReplaceCallback(nullptr);
|
||||
|
||||
_fieldFilter.reset(Core::InstallEventFilter(
|
||||
_field,
|
||||
[=](not_null<QEvent*> event) { return fieldFilter(event); }));
|
||||
_outerFilter.reset(Core::InstallEventFilter(
|
||||
outer,
|
||||
[=](not_null<QEvent*> event) { return outerFilter(event); }));
|
||||
const auto fieldCallback = [=](not_null<QEvent*> event) {
|
||||
return fieldFilter(event)
|
||||
? Core::EventFilter::Result::Cancel
|
||||
: Core::EventFilter::Result::Continue;
|
||||
};
|
||||
_fieldFilter.reset(Core::InstallEventFilter(_field, fieldCallback));
|
||||
|
||||
const auto outerCallback = [=](not_null<QEvent*> event) {
|
||||
return outerFilter(event)
|
||||
? Core::EventFilter::Result::Cancel
|
||||
: Core::EventFilter::Result::Continue;
|
||||
};
|
||||
_outerFilter.reset(Core::InstallEventFilter(outer, outerCallback));
|
||||
|
||||
QObject::connect(
|
||||
_field,
|
||||
&QTextEdit::textChanged,
|
||||
|
||||
@@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/unique_qptr.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
#include <QtWidgets/QTextEdit>
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
@@ -25,6 +25,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
FieldAutocomplete::FieldAutocomplete(
|
||||
QWidget *parent,
|
||||
not_null<Main::Session*> session)
|
||||
|
||||
@@ -25,6 +25,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_session_controller.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
namespace ChatHelpers {
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -26,6 +26,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_history.h"
|
||||
|
||||
#include <QtCore/QMimeData>
|
||||
#include <QtCore/QStack>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QTextBlock>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace {
|
||||
|
||||
using EditLinkAction = Ui::InputField::EditLinkAction;
|
||||
@@ -327,7 +333,7 @@ void SetClipboardText(
|
||||
const TextForMimeData &text,
|
||||
QClipboard::Mode mode) {
|
||||
if (auto data = MimeDataFromText(text)) {
|
||||
QApplication::clipboard()->setMimeData(data.release(), mode);
|
||||
QGuiApplication::clipboard()->setMimeData(data.release(), mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,18 +790,40 @@ void MessageLinksParser::apply(
|
||||
_list = std::move(parsed);
|
||||
}
|
||||
|
||||
void SetupSendWithoutSound(
|
||||
void SetupSendMenu(
|
||||
not_null<Ui::RpWidget*> button,
|
||||
Fn<bool()> enabled,
|
||||
Fn<void()> send) {
|
||||
Fn<SendMenuType()> type,
|
||||
Fn<void()> silent,
|
||||
Fn<void()> schedule) {
|
||||
if (!silent && !schedule) {
|
||||
return;
|
||||
}
|
||||
const auto menu = std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
|
||||
Core::InstallEventFilter(button, [=](not_null<QEvent*> e) {
|
||||
if (e->type() == QEvent::ContextMenu && enabled()) {
|
||||
*menu = base::make_unique_q<Ui::PopupMenu>(button);
|
||||
(*menu)->addAction(tr::lng_send_silent_message(tr::now), send);
|
||||
(*menu)->popup(QCursor::pos());
|
||||
return true;
|
||||
const auto showMenu = [=] {
|
||||
const auto now = type();
|
||||
if (now == SendMenuType::Disabled
|
||||
|| (!silent && now == SendMenuType::SilentOnly)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
*menu = base::make_unique_q<Ui::PopupMenu>(button);
|
||||
if (silent && now != SendMenuType::Reminder) {
|
||||
(*menu)->addAction(tr::lng_send_silent_message(tr::now), silent);
|
||||
}
|
||||
if (schedule && now != SendMenuType::SilentOnly) {
|
||||
(*menu)->addAction(
|
||||
(now == SendMenuType::Scheduled
|
||||
? tr::lng_schedule_message(tr::now)
|
||||
: tr::lng_reminder_message(tr::now)),
|
||||
schedule);
|
||||
}
|
||||
(*menu)->popup(QCursor::pos());
|
||||
return true;
|
||||
};
|
||||
Core::InstallEventFilter(button, [=](not_null<QEvent*> e) {
|
||||
if (e->type() == QEvent::ContextMenu && showMenu()) {
|
||||
return Core::EventFilter::Result::Cancel;
|
||||
}
|
||||
return Core::EventFilter::Result::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@@ -57,7 +59,7 @@ struct AutocompleteQuery {
|
||||
AutocompleteQuery ParseMentionHashtagBotCommandQuery(
|
||||
not_null<const Ui::InputField*> field);
|
||||
|
||||
class QtConnectionOwner {
|
||||
class QtConnectionOwner final {
|
||||
public:
|
||||
QtConnectionOwner(QMetaObject::Connection connection = {});
|
||||
QtConnectionOwner(QtConnectionOwner &&other);
|
||||
@@ -106,7 +108,15 @@ private:
|
||||
|
||||
};
|
||||
|
||||
void SetupSendWithoutSound(
|
||||
enum class SendMenuType {
|
||||
Disabled,
|
||||
SilentOnly,
|
||||
Scheduled,
|
||||
Reminder,
|
||||
};
|
||||
|
||||
void SetupSendMenu(
|
||||
not_null<Ui::RpWidget*> button,
|
||||
Fn<bool()> enabled,
|
||||
Fn<void()> send);
|
||||
Fn<SendMenuType()> type,
|
||||
Fn<void()> silent,
|
||||
Fn<void()> schedule);
|
||||
|
||||
@@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_history.h"
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
|
||||
namespace Stickers {
|
||||
namespace details {
|
||||
|
||||
@@ -43,7 +45,7 @@ private:
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kRefreshTimeout = TimeId(7200);
|
||||
constexpr auto kRefreshTimeout = 7200 * crl::time(1000);
|
||||
constexpr auto kClearSourceTimeout = 10 * crl::time(1000);
|
||||
|
||||
[[nodiscard]] QSize SingleSize() {
|
||||
@@ -317,14 +319,14 @@ EmojiImageLoader::EmojiImageLoader(
|
||||
}
|
||||
|
||||
QImage EmojiImageLoader::prepare(EmojiPtr emoji) {
|
||||
_images->ensureLoaded();
|
||||
const auto loaded = _images->ensureLoaded();
|
||||
const auto factor = cIntRetinaFactor();
|
||||
const auto side = st::largeEmojiSize + 2 * st::largeEmojiOutline;
|
||||
auto tinted = QImage(
|
||||
QSize(st::largeEmojiSize, st::largeEmojiSize) * factor,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
tinted.fill(Qt::white);
|
||||
{
|
||||
if (loaded) {
|
||||
QPainter p(&tinted);
|
||||
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||
_images->draw(
|
||||
@@ -338,7 +340,7 @@ QImage EmojiImageLoader::prepare(EmojiPtr emoji) {
|
||||
QSize(side, side) * factor,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
result.fill(Qt::transparent);
|
||||
{
|
||||
if (loaded) {
|
||||
QPainter p(&result);
|
||||
const auto delta = st::largeEmojiOutline * factor;
|
||||
const auto planar = std::array<QPoint, 4>{ {
|
||||
|
||||
@@ -34,6 +34,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_window.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
namespace ChatHelpers {
|
||||
namespace {
|
||||
|
||||
@@ -1574,6 +1576,8 @@ int StickersListWidget::megagroupSetInfoLeft() const {
|
||||
}
|
||||
|
||||
void StickersListWidget::paintMegagroupEmptySet(Painter &p, int y, bool buttonSelected) {
|
||||
p.setPen(st::emojiPanHeaderFg);
|
||||
|
||||
auto infoLeft = megagroupSetInfoLeft();
|
||||
_megagroupSetAbout.drawLeft(p, infoLeft, y, width() - infoLeft, width());
|
||||
|
||||
@@ -1890,7 +1894,18 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
_chosen.fire_copy(set.stickers[sticker->index].document);
|
||||
const auto document = set.stickers[sticker->index].document;
|
||||
if (e->modifiers() & Qt::ControlModifier) {
|
||||
if (document->sticker()
|
||||
&& document->sticker()->set.type() != mtpc_inputStickerSetEmpty) {
|
||||
_displayingSet = true;
|
||||
checkHideWithBox(StickerSetBox::Show(
|
||||
controller(),
|
||||
document));
|
||||
}
|
||||
} else {
|
||||
_chosen.fire_copy(document);
|
||||
}
|
||||
} else if (auto set = base::get_if<OverSet>(&pressed)) {
|
||||
Assert(set->section >= 0 && set->section < sets.size());
|
||||
displaySet(sets[set->section].id);
|
||||
@@ -2525,7 +2540,7 @@ void StickersListWidget::fillIcons(QList<StickerIcon> &icons) {
|
||||
}
|
||||
|
||||
bool StickersListWidget::preventAutoHide() {
|
||||
return _removingSetId != 0 || _displayingSetId != 0;
|
||||
return _removingSetId != 0 || _displayingSet != 0;
|
||||
}
|
||||
|
||||
void StickersListWidget::updateSelected() {
|
||||
@@ -2756,12 +2771,10 @@ void StickersListWidget::beforeHiding() {
|
||||
void StickersListWidget::displaySet(uint64 setId) {
|
||||
if (setId == Stickers::MegagroupSetId) {
|
||||
if (_megagroupSet->canEditStickers()) {
|
||||
_displayingSetId = setId;
|
||||
auto box = Ui::show(Box<StickersBox>(_megagroupSet));
|
||||
connect(box, &QObject::destroyed, this, [this] {
|
||||
_displayingSetId = 0;
|
||||
_checkForHide.fire({});
|
||||
});
|
||||
_displayingSet = true;
|
||||
checkHideWithBox(Ui::show(
|
||||
Box<StickersBox>(_megagroupSet),
|
||||
LayerOption::KeepOther).data());
|
||||
return;
|
||||
} else if (_megagroupSet->mgInfo->stickerSet.type() == mtpc_inputStickerSetID) {
|
||||
setId = _megagroupSet->mgInfo->stickerSet.c_inputStickerSetID().vid().v;
|
||||
@@ -2772,17 +2785,23 @@ void StickersListWidget::displaySet(uint64 setId) {
|
||||
auto &sets = session().data().stickerSets();
|
||||
auto it = sets.constFind(setId);
|
||||
if (it != sets.cend()) {
|
||||
_displayingSetId = setId;
|
||||
auto box = Ui::show(
|
||||
_displayingSet = true;
|
||||
checkHideWithBox(Ui::show(
|
||||
Box<StickerSetBox>(controller(), Stickers::inputSetId(*it)),
|
||||
LayerOption::KeepOther);
|
||||
connect(box, &QObject::destroyed, this, [this] {
|
||||
_displayingSetId = 0;
|
||||
_checkForHide.fire({});
|
||||
});
|
||||
LayerOption::KeepOther).data());
|
||||
}
|
||||
}
|
||||
|
||||
void StickersListWidget::checkHideWithBox(QPointer<BoxContent> box) {
|
||||
if (!box) {
|
||||
return;
|
||||
}
|
||||
connect(box, &QObject::destroyed, this, [=] {
|
||||
_displayingSet = false;
|
||||
_checkForHide.fire({});
|
||||
});
|
||||
}
|
||||
|
||||
void StickersListWidget::installSet(uint64 setId) {
|
||||
auto &sets = session().data().stickerSets();
|
||||
auto it = sets.constFind(setId);
|
||||
|
||||
@@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/variant.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
class BoxContent;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@@ -201,6 +203,7 @@ private:
|
||||
|
||||
void setSection(Section section);
|
||||
void displaySet(uint64 setId);
|
||||
void checkHideWithBox(QPointer<BoxContent> box);
|
||||
void installSet(uint64 setId);
|
||||
void removeMegagroupSet(bool locally);
|
||||
void removeSet(uint64 setId);
|
||||
@@ -310,7 +313,7 @@ private:
|
||||
|
||||
Section _section = Section::Stickers;
|
||||
|
||||
uint64 _displayingSetId = 0;
|
||||
bool _displayingSet = false;
|
||||
uint64 _removingSetId = 0;
|
||||
|
||||
Footer *_footer = nullptr;
|
||||
|
||||
@@ -26,20 +26,27 @@ constexpr auto kDelayedHideTimeoutMs = 3000;
|
||||
|
||||
TabbedPanel::TabbedPanel(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: TabbedPanel(
|
||||
parent,
|
||||
controller,
|
||||
object_ptr<TabbedSelector>(nullptr, controller)) {
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<TabbedSelector*> selector)
|
||||
: TabbedPanel(parent, controller, { nullptr }, selector) {
|
||||
}
|
||||
|
||||
TabbedPanel::TabbedPanel(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
object_ptr<TabbedSelector> selector)
|
||||
: TabbedPanel(parent, controller, std::move(selector), nullptr) {
|
||||
}
|
||||
|
||||
TabbedPanel::TabbedPanel(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
object_ptr<TabbedSelector> ownedSelector,
|
||||
TabbedSelector *nonOwnedSelector)
|
||||
: RpWidget(parent)
|
||||
, _controller(controller)
|
||||
, _selector(std::move(selector))
|
||||
, _ownedSelector(std::move(ownedSelector))
|
||||
, _selector(nonOwnedSelector ? nonOwnedSelector : _ownedSelector.data())
|
||||
, _heightRatio(st::emojiPanHeightRatio)
|
||||
, _minContentHeight(st::emojiPanMinHeight)
|
||||
, _maxContentHeight(st::emojiPanMaxHeight) {
|
||||
@@ -103,6 +110,15 @@ TabbedPanel::TabbedPanel(
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, false);
|
||||
|
||||
hideChildren();
|
||||
hide();
|
||||
}
|
||||
|
||||
not_null<TabbedSelector*> TabbedPanel::selector() const {
|
||||
return _selector;
|
||||
}
|
||||
|
||||
bool TabbedPanel::isSelectorStolen() const {
|
||||
return (_selector->parent() != this);
|
||||
}
|
||||
|
||||
void TabbedPanel::moveBottomRight(int bottom, int right) {
|
||||
@@ -365,17 +381,6 @@ void TabbedPanel::toggleAnimated() {
|
||||
}
|
||||
}
|
||||
|
||||
object_ptr<TabbedSelector> TabbedPanel::takeSelector() {
|
||||
if (!isHidden() && !_hiding) {
|
||||
startOpacityAnimation(true);
|
||||
}
|
||||
return std::move(_selector);
|
||||
}
|
||||
|
||||
QPointer<TabbedSelector> TabbedPanel::getSelector() const {
|
||||
return _selector.data();
|
||||
}
|
||||
|
||||
void TabbedPanel::hideFinished() {
|
||||
hide();
|
||||
_a_show.stop();
|
||||
@@ -449,6 +454,10 @@ bool TabbedPanel::overlaps(const QRect &globalRect) const {
|
||||
|| inner.marginsRemoved(QMargins(0, st::buttonRadius, 0, st::buttonRadius)).contains(testRect);
|
||||
}
|
||||
|
||||
TabbedPanel::~TabbedPanel() = default;
|
||||
TabbedPanel::~TabbedPanel() {
|
||||
if (!_ownedSelector) {
|
||||
_controller->takeTabbedSelectorOwnershipFrom(this);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ChatHelpers
|
||||
|
||||
@@ -25,14 +25,18 @@ class TabbedSelector;
|
||||
|
||||
class TabbedPanel : public Ui::RpWidget {
|
||||
public:
|
||||
TabbedPanel(QWidget *parent, not_null<Window::SessionController*> controller);
|
||||
TabbedPanel(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<TabbedSelector*> selector);
|
||||
TabbedPanel(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
object_ptr<TabbedSelector> selector);
|
||||
|
||||
object_ptr<TabbedSelector> takeSelector();
|
||||
QPointer<TabbedSelector> getSelector() const;
|
||||
[[nodiscard]] bool isSelectorStolen() const;
|
||||
[[nodiscard]] not_null<TabbedSelector*> selector() const;
|
||||
|
||||
void moveBottomRight(int bottom, int right);
|
||||
void setDesiredHeightValues(
|
||||
float64 ratio,
|
||||
@@ -62,6 +66,12 @@ protected:
|
||||
bool eventFilter(QObject *obj, QEvent *e) override;
|
||||
|
||||
private:
|
||||
TabbedPanel(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
object_ptr<TabbedSelector> ownedSelector,
|
||||
TabbedSelector *nonOwnedSelector);
|
||||
|
||||
void hideByTimerOrLeave();
|
||||
void moveByBottom();
|
||||
bool isDestroying() const {
|
||||
@@ -87,8 +97,9 @@ private:
|
||||
bool preventAutoHide() const;
|
||||
void updateContentHeight();
|
||||
|
||||
not_null<Window::SessionController*> _controller;
|
||||
object_ptr<TabbedSelector> _selector;
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
const object_ptr<TabbedSelector> _ownedSelector = { nullptr };
|
||||
const not_null<TabbedSelector*> _selector;
|
||||
|
||||
int _contentMaxHeight = 0;
|
||||
int _contentHeight = 0;
|
||||
|
||||
@@ -7,56 +7,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "chat_helpers/tabbed_section.h"
|
||||
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "chat_helpers/tabbed_selector.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
namespace ChatHelpers {
|
||||
|
||||
TabbedMemento::TabbedMemento(
|
||||
object_ptr<TabbedSelector> selector,
|
||||
Fn<void(object_ptr<TabbedSelector>)> returnMethod)
|
||||
: _selector(std::move(selector))
|
||||
, _returnMethod(std::move(returnMethod)) {
|
||||
}
|
||||
|
||||
object_ptr<Window::SectionWidget> TabbedMemento::createWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
Window::Column column,
|
||||
const QRect &geometry) {
|
||||
auto result = object_ptr<TabbedSection>(
|
||||
parent,
|
||||
controller,
|
||||
std::move(_selector),
|
||||
std::move(_returnMethod));
|
||||
auto result = object_ptr<TabbedSection>(parent, controller);
|
||||
result->setGeometry(geometry);
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
TabbedMemento::~TabbedMemento() {
|
||||
if (_returnMethod && _selector) {
|
||||
_returnMethod(std::move(_selector));
|
||||
}
|
||||
}
|
||||
|
||||
TabbedSection::TabbedSection(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: TabbedSection(
|
||||
parent,
|
||||
controller,
|
||||
object_ptr<TabbedSelector>(this, controller),
|
||||
Fn<void(object_ptr<TabbedSelector>)>()) {
|
||||
}
|
||||
|
||||
TabbedSection::TabbedSection(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
object_ptr<TabbedSelector> selector,
|
||||
Fn<void(object_ptr<TabbedSelector>)> returnMethod)
|
||||
: Window::SectionWidget(parent, controller)
|
||||
, _selector(std::move(selector))
|
||||
, _returnMethod(std::move(returnMethod)) {
|
||||
, _selector(controller->tabbedSelector()) {
|
||||
_selector->setParent(this);
|
||||
_selector->setRoundRadius(0);
|
||||
_selector->setGeometry(rect());
|
||||
@@ -80,14 +51,6 @@ void TabbedSection::resizeEvent(QResizeEvent *e) {
|
||||
_selector->setGeometry(rect());
|
||||
}
|
||||
|
||||
object_ptr<TabbedSelector> TabbedSection::takeSelector() {
|
||||
_selector->beforeHiding();
|
||||
return std::move(_selector);
|
||||
}
|
||||
|
||||
QPointer<TabbedSelector> TabbedSection::getSelector() const {
|
||||
return _selector.data();
|
||||
}
|
||||
bool TabbedSection::showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) {
|
||||
@@ -104,9 +67,7 @@ QRect TabbedSection::rectForFloatPlayer() const {
|
||||
|
||||
TabbedSection::~TabbedSection() {
|
||||
beforeHiding();
|
||||
if (_returnMethod) {
|
||||
_returnMethod(takeSelector());
|
||||
}
|
||||
controller()->takeTabbedSelectorOwnershipFrom(this);
|
||||
}
|
||||
|
||||
} // namespace ChatHelpers
|
||||
|
||||
@@ -16,9 +16,7 @@ class TabbedSelector;
|
||||
|
||||
class TabbedMemento : public Window::SectionMemento {
|
||||
public:
|
||||
TabbedMemento(
|
||||
object_ptr<TabbedSelector> selector,
|
||||
Fn<void(object_ptr<TabbedSelector>)> returnMethod);
|
||||
TabbedMemento() = default;
|
||||
TabbedMemento(TabbedMemento &&other) = default;
|
||||
TabbedMemento &operator=(TabbedMemento &&other) = default;
|
||||
|
||||
@@ -28,12 +26,6 @@ public:
|
||||
Window::Column column,
|
||||
const QRect &geometry) override;
|
||||
|
||||
~TabbedMemento();
|
||||
|
||||
private:
|
||||
object_ptr<TabbedSelector> _selector;
|
||||
Fn<void(object_ptr<TabbedSelector>)> _returnMethod;
|
||||
|
||||
};
|
||||
|
||||
class TabbedSection : public Window::SectionWidget {
|
||||
@@ -41,18 +33,10 @@ public:
|
||||
TabbedSection(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller);
|
||||
TabbedSection(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
object_ptr<TabbedSelector> selector,
|
||||
Fn<void(object_ptr<TabbedSelector>)> returnMethod);
|
||||
|
||||
void beforeHiding();
|
||||
void afterShown();
|
||||
|
||||
object_ptr<TabbedSelector> takeSelector();
|
||||
QPointer<TabbedSelector> getSelector() const;
|
||||
|
||||
bool showInternal(
|
||||
not_null<Window::SectionMemento*> memento,
|
||||
const Window::SectionShow ¶ms) override;
|
||||
@@ -73,8 +57,7 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
object_ptr<TabbedSelector> _selector;
|
||||
Fn<void(object_ptr<TabbedSelector>)> _returnMethod;
|
||||
const not_null<TabbedSelector*> _selector;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "chat_helpers/stickers_list_widget.h"
|
||||
#include "chat_helpers/gifs_list_widget.h"
|
||||
#include "chat_helpers/stickers.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
@@ -21,10 +20,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_session_controller.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_session.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwindow.h"
|
||||
#include "observer_peer.h"
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
namespace ChatHelpers {
|
||||
|
||||
@@ -291,8 +292,8 @@ TabbedSelector::TabbedSelector(
|
||||
createTab(SelectorTab::Gifs),
|
||||
} }
|
||||
, _currentTabType(full()
|
||||
? session().settings().selectorTab()
|
||||
: SelectorTab::Emoji) {
|
||||
? session().settings().selectorTab()
|
||||
: SelectorTab::Emoji) {
|
||||
resize(st::emojiPanWidth, st::emojiPanMaxHeight);
|
||||
|
||||
for (auto &tab : _tabs) {
|
||||
@@ -364,10 +365,16 @@ TabbedSelector::TabbedSelector(
|
||||
stickers()->showStickerSet(setId);
|
||||
_showRequests.fire({});
|
||||
}, lifetime());
|
||||
|
||||
session().data().stickersUpdated(
|
||||
) | rpl::start_with_next([=] {
|
||||
refreshStickers();
|
||||
}, lifetime());
|
||||
}
|
||||
//setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
setAttribute(Qt::WA_OpaquePaintEvent, false);
|
||||
showAll();
|
||||
hide();
|
||||
}
|
||||
|
||||
TabbedSelector::~TabbedSelector() = default;
|
||||
@@ -651,13 +658,6 @@ void TabbedSelector::afterShown() {
|
||||
}
|
||||
}
|
||||
|
||||
void TabbedSelector::showMegagroupSet(ChannelData *megagroup) {
|
||||
if (!full()) {
|
||||
return;
|
||||
}
|
||||
stickers()->showMegagroupSet(megagroup);
|
||||
}
|
||||
|
||||
void TabbedSelector::setCurrentPeer(PeerData *peer) {
|
||||
if (!full()) {
|
||||
return;
|
||||
@@ -665,6 +665,7 @@ void TabbedSelector::setCurrentPeer(PeerData *peer) {
|
||||
gifs()->setInlineQueryPeer(peer);
|
||||
_currentPeer = peer;
|
||||
checkRestrictedPeer();
|
||||
stickers()->showMegagroupSet(peer ? peer->asMegagroup() : nullptr);
|
||||
}
|
||||
|
||||
void TabbedSelector::checkRestrictedPeer() {
|
||||
|
||||
@@ -70,7 +70,6 @@ public:
|
||||
|
||||
void setRoundRadius(int radius);
|
||||
void refreshStickers();
|
||||
void showMegagroupSet(ChannelData *megagroup);
|
||||
void setCurrentPeer(PeerData *peer);
|
||||
|
||||
void hideFinished();
|
||||
|
||||
@@ -28,6 +28,11 @@ namespace {
|
||||
|
||||
constexpr int kErrorBadIconSize = 861;
|
||||
|
||||
const auto kMustBeContrast = std::map<QString, QString>{
|
||||
{ "dialogsMenuIconFg", "dialogsBg" },
|
||||
{ "windowBoldFg", "windowBg" },
|
||||
};
|
||||
|
||||
// crc32 hash, taken somewhere from the internet
|
||||
|
||||
class Crc32Table {
|
||||
@@ -797,6 +802,15 @@ void palette::finalize() {\n\
|
||||
auto count = indexInPalette;
|
||||
auto checksum = hashCrc32(checksumString.constData(), checksumString.size());
|
||||
|
||||
source_->stream() << "\n\n";
|
||||
for (const auto &[over, under] : kMustBeContrast) {
|
||||
const auto overIndex = paletteIndices_.find(over);
|
||||
const auto underIndex = paletteIndices_.find(under);
|
||||
if (overIndex == paletteIndices_.end() || underIndex == paletteIndices_.end()) {
|
||||
return false;
|
||||
}
|
||||
source_->stream() << "\tinternal::EnsureContrast(*data(" << overIndex->second << "), *data(" << underIndex->second << "));\n";
|
||||
}
|
||||
source_->stream() << "\
|
||||
}\n\
|
||||
\n\
|
||||
|
||||
@@ -70,18 +70,6 @@ enum {
|
||||
ChoosePeerByDragTimeout = 1000, // 1 second mouse not moved to choose dialog when dragging a file
|
||||
};
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
inline const GUID &cGUID() {
|
||||
#ifndef OS_MAC_STORE
|
||||
static const GUID gGuid = { 0x87a94ab0, 0xe370, 0x4cde, { 0x98, 0xd3, 0xac, 0xc1, 0x10, 0xc5, 0x96, 0x7d } };
|
||||
#else // OS_MAC_STORE
|
||||
static const GUID gGuid = { 0xe51fb841, 0x8c0b, 0x4ef9, { 0x9e, 0x9e, 0x5a, 0x0, 0x78, 0x56, 0x76, 0x27 } };
|
||||
#endif // OS_MAC_STORE
|
||||
|
||||
return gGuid;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline const char *cGUIDStr() {
|
||||
#ifndef OS_MAC_STORE
|
||||
static const char *gGuidStr = "{87A94AB0-E370-4cde-98D3-ACC110C5967D}";
|
||||
|
||||
@@ -62,6 +62,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/share_box.h"
|
||||
|
||||
#include <QtWidgets/QDesktopWidget>
|
||||
#include <QtCore/QMimeDatabase>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
||||
namespace Core {
|
||||
namespace {
|
||||
|
||||
@@ -138,7 +143,7 @@ Application::~Application() {
|
||||
stopWebLoadManager();
|
||||
App::deinitMedia();
|
||||
|
||||
Window::Theme::Unload();
|
||||
Window::Theme::Uninitialize();
|
||||
|
||||
Media::Player::finish(_audio.get());
|
||||
style::stopManager();
|
||||
@@ -279,6 +284,14 @@ void Application::showDocument(not_null<DocumentData*> document, HistoryItem *it
|
||||
}
|
||||
}
|
||||
|
||||
void Application::showTheme(
|
||||
not_null<DocumentData*> document,
|
||||
const Data::CloudTheme &cloud) {
|
||||
_mediaView->showTheme(document, cloud);
|
||||
_mediaView->activateWindow();
|
||||
_mediaView->setFocus();
|
||||
}
|
||||
|
||||
PeerData *Application::ui_getPeerForMouseAction() {
|
||||
if (_mediaView && !_mediaView->isHidden()) {
|
||||
return _mediaView->ui_getPeerForMouseAction();
|
||||
@@ -336,6 +349,10 @@ bool Application::eventFilter(QObject *object, QEvent *e) {
|
||||
return QObject::eventFilter(object, e);
|
||||
}
|
||||
|
||||
void Application::saveSettingsDelayed(crl::time delay) {
|
||||
_saveSettingsTimer.callOnce(delay);
|
||||
}
|
||||
|
||||
void Application::setCurrentProxy(
|
||||
const ProxyData &proxy,
|
||||
ProxyData::Settings settings) {
|
||||
@@ -381,6 +398,7 @@ void Application::startLocalStorage() {
|
||||
}
|
||||
}
|
||||
});
|
||||
_saveSettingsTimer.setCallback([=] { Local::writeSettings(); });
|
||||
}
|
||||
|
||||
void Application::forceLogOut(const TextWithEntities &explanation) {
|
||||
@@ -583,6 +601,10 @@ void Application::lockByPasscode() {
|
||||
|
||||
void Application::unlockPasscode() {
|
||||
clearPasscodeLock();
|
||||
if (!activeAccount().mtp()) {
|
||||
// We unlocked initial passcode, so we just start mtproto.
|
||||
activeAccount().startMtp();
|
||||
}
|
||||
if (_window) {
|
||||
_window->clearPasscodeLock();
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/observer.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "mtproto/auth_key.h"
|
||||
#include "base/observer.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
class MainWindow;
|
||||
@@ -67,6 +68,10 @@ class Translator;
|
||||
class CloudManager;
|
||||
} // namespace Lang
|
||||
|
||||
namespace Data {
|
||||
struct CloudTheme;
|
||||
} // namespace Data
|
||||
|
||||
namespace Core {
|
||||
|
||||
class Launcher;
|
||||
@@ -94,9 +99,6 @@ public:
|
||||
bool closeActiveWindow();
|
||||
bool minimizeActiveWindow();
|
||||
QWidget *getFileDialogParent();
|
||||
QWidget *getGlobalShortcutParent() {
|
||||
return &_globalShortcutParent;
|
||||
}
|
||||
|
||||
// Media view interface.
|
||||
void checkMediaViewActivation();
|
||||
@@ -105,6 +107,9 @@ public:
|
||||
void showPhoto(not_null<PhotoData*> photo, HistoryItem *item);
|
||||
void showPhoto(not_null<PhotoData*> photo, not_null<PeerData*> item);
|
||||
void showDocument(not_null<DocumentData*> document, HistoryItem *item);
|
||||
void showTheme(
|
||||
not_null<DocumentData*> document,
|
||||
const Data::CloudTheme &cloud);
|
||||
PeerData *ui_getPeerForMouseAction();
|
||||
|
||||
QPoint getPointForCallPanelCenter() const;
|
||||
@@ -115,7 +120,13 @@ public:
|
||||
return _logoNoMargin;
|
||||
}
|
||||
|
||||
// MTProto components.
|
||||
[[nodiscard]] Settings &settings() {
|
||||
return _settings;
|
||||
}
|
||||
void moveSettingsFrom(Settings &&other);
|
||||
void saveSettingsDelayed(crl::time delay = kDefaultSaveDelay);
|
||||
|
||||
// Dc options and proxy.
|
||||
MTP::DcOptions *dcOptions() {
|
||||
return _dcOptions.get();
|
||||
}
|
||||
@@ -221,6 +232,8 @@ protected:
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
|
||||
private:
|
||||
static constexpr auto kDefaultSaveDelay = crl::time(1000);
|
||||
|
||||
friend bool IsAppLaunched();
|
||||
friend Application &App();
|
||||
|
||||
@@ -251,8 +264,7 @@ private:
|
||||
// Some fields are just moved from the declaration.
|
||||
struct Private;
|
||||
const std::unique_ptr<Private> _private;
|
||||
|
||||
QWidget _globalShortcutParent;
|
||||
Settings _settings;
|
||||
|
||||
const std::unique_ptr<Storage::Databases> _databases;
|
||||
const std::unique_ptr<Ui::Animations::Manager> _animationsManager;
|
||||
@@ -276,6 +288,7 @@ private:
|
||||
std::unique_ptr<Window::TermsLock> _termsLock;
|
||||
|
||||
base::DelayedCallTimer _callDelayedTimer;
|
||||
base::Timer _saveSettingsTimer;
|
||||
|
||||
struct LeaveSubscription {
|
||||
LeaveSubscription(
|
||||
|
||||
@@ -51,7 +51,15 @@ std::map<int, const char*> BetaLogs() {
|
||||
"\xE2\x80\xA2 Use strikethrough and underline formatting.\n"
|
||||
|
||||
"\xE2\x80\xA2 Bug fixes and other minor improvements."
|
||||
}
|
||||
},
|
||||
{
|
||||
1008005,
|
||||
"\xE2\x80\xA2 Create new themes based on your color and wallpaper choices.\n"
|
||||
|
||||
"\xE2\x80\xA2 Share your themes with other users via links.\n"
|
||||
|
||||
"\xE2\x80\xA2 Update your theme for all its users when you change something.\n"
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_session.h"
|
||||
|
||||
#include <QtGui/QDesktopServices>
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace {
|
||||
|
||||
bool UrlRequiresConfirmation(const QUrl &url) {
|
||||
|
||||
51
Telegram/SourceFiles/core/core_settings.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
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 "core/core_settings.h"
|
||||
|
||||
#include "storage/serialize_common.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
Settings::Variables::Variables() {
|
||||
}
|
||||
|
||||
QByteArray Settings::serialize() const {
|
||||
const auto themesAccentColors = _variables.themesAccentColors.serialize();
|
||||
auto size = Serialize::bytearraySize(themesAccentColors);
|
||||
|
||||
auto result = QByteArray();
|
||||
result.reserve(size);
|
||||
{
|
||||
QDataStream stream(&result, QIODevice::WriteOnly);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
stream << themesAccentColors;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Settings::constructFromSerialized(const QByteArray &serialized) {
|
||||
if (serialized.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDataStream stream(serialized);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
QByteArray themesAccentColors;
|
||||
|
||||
stream >> themesAccentColors;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("App Error: "
|
||||
"Bad data for Core::Settings::constructFromSerialized()"));
|
||||
return;
|
||||
}
|
||||
if (!_variables.themesAccentColors.setFromSerialized(themesAccentColors)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
40
Telegram/SourceFiles/core/core_settings.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
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 "window/themes/window_themes_embedded.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class Settings final {
|
||||
public:
|
||||
void moveFrom(Settings &&other) {
|
||||
_variables = std::move(other._variables);
|
||||
}
|
||||
[[nodiscard]] QByteArray serialize() const;
|
||||
void constructFromSerialized(const QByteArray &serialized);
|
||||
|
||||
void setThemesAccentColors(Window::Theme::AccentColors &&colors) {
|
||||
_variables.themesAccentColors = std::move(colors);
|
||||
}
|
||||
[[nodiscard]] Window::Theme::AccentColors &themesAccentColors() {
|
||||
return _variables.themesAccentColors;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Variables {
|
||||
Variables();
|
||||
|
||||
Window::Theme::AccentColors themesAccentColors;
|
||||
};
|
||||
|
||||
Variables _variables;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
@@ -15,6 +15,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "platform/platform_specific.h"
|
||||
#include "base/zlib_help.h"
|
||||
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtGui/QDesktopServices>
|
||||
#include <QtCore/QStandardPaths>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kDefaultProxyPort = 80;
|
||||
|
||||
@@ -7,6 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtWidgets/QTextEdit>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QCheckBox>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtNetwork/QHttpMultiPart>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
|
||||
namespace Core {
|
||||
class Launcher;
|
||||
} // namespace Core
|
||||
|
||||
@@ -11,20 +11,28 @@ namespace Core {
|
||||
|
||||
EventFilter::EventFilter(
|
||||
not_null<QObject*> parent,
|
||||
Fn<bool(not_null<QEvent*>)> filter)
|
||||
not_null<QObject*> object,
|
||||
Fn<EventFilter::Result(not_null<QEvent*>)> filter)
|
||||
: QObject(parent)
|
||||
, _filter(std::move(filter)) {
|
||||
parent->installEventFilter(this);
|
||||
object->installEventFilter(this);
|
||||
}
|
||||
|
||||
bool EventFilter::eventFilter(QObject *watched, QEvent *event) {
|
||||
return _filter(event);
|
||||
return (_filter(event) == Result::Cancel);
|
||||
}
|
||||
|
||||
not_null<QObject*> InstallEventFilter(
|
||||
not_null<QObject*> object,
|
||||
Fn<bool(not_null<QEvent*>)> filter) {
|
||||
return new EventFilter(object, std::move(filter));
|
||||
Fn<EventFilter::Result(not_null<QEvent*>)> filter) {
|
||||
return InstallEventFilter(object, object, std::move(filter));
|
||||
}
|
||||
|
||||
not_null<QObject*> InstallEventFilter(
|
||||
not_null<QObject*> context,
|
||||
not_null<QObject*> object,
|
||||
Fn<EventFilter::Result(not_null<QEvent*>)> filter) {
|
||||
return new EventFilter(context, object, std::move(filter));
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||