Compare commits

...

196 Commits

Author SHA1 Message Date
John Preston
a87529b8c9 Version 1.8.10.
- Bug fixes and other minor improvements.
2019-10-01 23:59:46 +03:00
John Preston
0c30bbf40a Fix file reference refresh in stickers / GIFs.
Fixes #6592.
2019-09-30 13:25:09 +03:00
John Preston
f75bfb4369 Version 1.8.9.
- Bug fixes and other minor improvements.
2019-09-27 20:04:25 +03:00
John Preston
0c34ba638c Don't show revoke checkbox in Saved Messages. 2019-09-27 20:01:36 +03:00
John Preston
4af9c801ae Don't show sticker suggestions if can't send.
Fixes #6581.
2019-09-27 19:56:54 +03:00
John Preston
e06bf16980 Improve name / title limits. 2019-09-27 19:46:27 +03:00
John Preston
1018745b0b Fix userpic display in custom notifications.
Fixes #6532.
2019-09-27 19:36:19 +03:00
John Preston
47b157bf32 Fix possible crash in custom notifications. 2019-09-27 18:53:44 +03:00
John Preston
09547bd6d5 Fix possible crash in scheduled messages. 2019-09-27 18:49:49 +03:00
RadRussianRus
d17f21c10d Use exact version of libxkbcommon in Travis build 2019-09-27 18:05:26 +03:00
udf
f11339361d Make "All Users" checkbox behave like the "All Actions" checkbox 2019-09-27 18:04:50 +03:00
John Preston
04b9d4bdb5 Fix animated emoji reloading. 2019-09-11 14:00:18 +03:00
John Preston
1d8d2b6251 Fix theme link preview title. 2019-09-10 18:30:47 +03:00
John Preston
190bf8fc5f Version 1.8.8.
- Create new themes based on your color and wallpaper choices.
- Share your themes with other users via links.
- Update your theme for all its users when you change something.
2019-09-10 14:22:20 +03:00
John Preston
f463d3ec6d Divide autoupdate paths for OS versions. 2019-09-10 14:22:20 +03:00
23rd
f9d10094ac Added warning when try to attach new media while editing message. 2019-09-10 14:22:20 +03:00
RadRussianRus
9fc87c3cb8 Fix last message tail drawing in theme preview 2019-09-10 10:48:20 +03:00
Sean
0d9c30423f Add chrpath to CMake instruction
Ref: 02a8b69
2019-09-10 10:13:51 +03:00
John Preston
c4bc76c022 Beta version 1.8.7.
- Bug fixes and other minor improvements.
2019-09-10 00:52:34 +03:00
John Preston
241f5e1d26 Make theme preview fit in minimal window size. 2019-09-10 00:50:15 +03:00
John Preston
76f0abecfd Show sticker set on Ctrl+Click in stickers list. 2019-09-10 00:48:34 +03:00
John Preston
ca45fb617e Fix applying background after theme edit. 2019-09-10 00:36:16 +03:00
John Preston
dfd63e66ff Add import theme to the editor. 2019-09-09 23:58:41 +03:00
John Preston
d85f162bff Fix saving cloud themes from media viewer. 2019-09-09 23:58:30 +03:00
John Preston
1d4fbc64e2 Ensure theme list radiobutton contrast. 2019-09-09 23:25:54 +03:00
John Preston
9fd32fc85b Ensure contrast in critical theme parts. 2019-09-09 23:02:24 +03:00
John Preston
f7f195eb98 Fix transfer ownership button visibility.
Regression was introduced in 569340c7d3.
2019-09-09 21:07:12 +03:00
John Preston
12c7bd8ee1 Suggest theme title based on accent color. 2019-09-09 20:27:07 +03:00
John Preston
cd5ef069c0 Ask to unlock Telegram before saving theme. 2019-09-09 20:26:53 +03:00
John Preston
f90fdce422 Fix crash on quit from passcode with a box. 2019-09-09 18:54:38 +03:00
John Preston
423254f7eb Fix theme editor closing in a small window.
Fixes #6510.
2019-09-09 18:37:04 +03:00
John Preston
c9c0d74b68 Fix build for Linux. 2019-09-09 17:50:26 +03:00
John Preston
8bf6013342 Add 'Export theme' / 'Show palette file' menu. 2019-09-09 17:44:08 +03:00
John Preston
c1ab1acd44 Fix crash in scheduled messages 'Send Now'. 2019-09-09 17:43:39 +03:00
John Preston
90b955534a Beta version 1.8.6: Improve theme preview. 2019-09-09 16:13:16 +03:00
John Preston
61c7bf2f5e Beta version 1.8.6.
- Bug fixes and other minor improvements.
2019-09-09 14:56:55 +03:00
John Preston
ee5423762a Generate previews for cloud theme links. 2019-09-09 14:56:05 +03:00
John Preston
6d29dc3b36 Apply themes in settings without preview. 2019-09-09 12:13:07 +03:00
John Preston
aa10934e85 Fix messing up accent color if editor is opened. 2019-09-09 12:02:06 +03:00
John Preston
9ff1fbcf47 Support theme document file origins. 2019-09-09 11:51:07 +03:00
John Preston
51c1dc20e1 Unregister randomId if send message fails. 2019-09-09 11:13:41 +03:00
John Preston
3a3bf84cfc Fix crash in SeparatePanel destruction.
We should destroy layers before panel widget destructor started.
We do it already for MainWindow in clearWidgetsHook.
2019-09-09 10:52:03 +03:00
John Preston
037b936613 Fix possible crash with emoji load failure. 2019-09-09 10:45:18 +03:00
John Preston
b631d09a40 Fix possible crash in event loop nesting. 2019-09-09 10:45:18 +03:00
John Zimmermann
fd4f384c3b Allow GDK wayland backend when Telegram is running under Wayland
and make sure that it never tries to load gtk2 under wayland, it can
lead to Telegram crashing
2019-09-09 10:25:01 +03:00
RadRussianRus
428a501bac Show Beginning button only for message date
Also hide button when you're already at beginning.
2019-09-09 10:17:23 +03:00
RadRussianRus
3ca57ae50d Added "Beginning" button to calendar 2019-09-09 10:17:23 +03:00
RadRussianRus
f1eddcd584 Make link preview title clickable 2019-09-09 10:15:32 +03:00
Sameer Hoosen
f979df3dfe Allow searching for messages by a user who has left a chat (#6417)
Fixes #5667
2019-09-09 10:05:29 +03:00
John Preston
eebcdb842d Generate previews async in cloud themes list. 2019-09-09 09:59:57 +03:00
John Preston
048658f838 Fix deleting theme from the list. 2019-09-09 08:54:17 +03:00
John Preston
8fd17e2e8f Fix theme with background with colorizer. 2019-09-09 08:53:16 +03:00
John Preston
5c13214244 Beta version 1.8.5: Fix build for old OS X. 2019-09-08 23:42:40 +03:00
John Preston
12a020cd09 Beta version 1.8.5: Fix build for Linux. 2019-09-08 23:38:45 +03:00
John Preston
b98e02f326 Beta version 1.8.5: Fix build on macOS. 2019-09-08 23:04:25 +03:00
John Preston
9a521c5340 Beta version 1.8.5.
- Create new themes based on your color and wallpaper choices.
- Share your themes with other users via links.
- Update your theme for all its users when you change something.
2019-09-08 21:38:58 +03:00
John Preston
5f26e92f5c Fix scheduled messages from ShareBox.
Fixes #6497.
2019-09-08 21:26:17 +03:00
John Preston
3d78c637a2 Strip editor information from palette. 2019-09-08 21:21:54 +03:00
John Preston
2ab2eed633 Push new saved theme to the list. 2019-09-08 21:05:35 +03:00
John Preston
bb7018424a Copy adjusted color values to editor. 2019-09-08 21:00:31 +03:00
John Preston
a770b5d4cd Allow share and remove cloud themes. 2019-09-08 20:32:25 +03:00
John Preston
c92a9585e1 Check changes when saving theme. 2019-09-08 19:29:43 +03:00
John Preston
cedb2d31af Check palette changes on editor cancel. 2019-09-08 16:40:15 +03:00
John Preston
95da2dbc34 Move create theme button to three-dot menu. 2019-09-08 15:47:22 +03:00
John Preston
f9b2a8d6ac Show progress in theme saving. 2019-09-08 14:52:42 +03:00
John Preston
a773ad7b02 Fix reverting edited theme values. 2019-09-06 19:31:07 +03:00
John Preston
32287a51f9 Don't show warning on theme autoupdate. 2019-09-06 19:31:07 +03:00
John Preston
37a4c79c81 Open theme preview after loading. 2019-09-06 19:31:07 +03:00
John Preston
95ee17bd54 Optimize theme edit data clearing. 2019-09-06 19:31:07 +03:00
John Preston
469c6770fb Handle t.me/addtheme links. 2019-09-06 19:31:07 +03:00
John Preston
e38123cc48 Send theme install requests. 2019-09-06 19:31:07 +03:00
John Preston
e3d7bf771f Update current theme in realtime. 2019-09-06 19:31:07 +03:00
John Preston
1cda90c3c5 Check fields before uploading theme. 2019-09-06 19:31:07 +03:00
John Preston
9c86f0e0a5 Save themes, apply and close editor on save. 2019-09-06 19:31:07 +03:00
John Preston
910f16312c Show not supported themes placeholders. 2019-09-06 19:31:07 +03:00
23rd
639b4bdd27 Fixed badge update during theme testing. 2019-09-06 19:31:07 +03:00
John Preston
4951eeac98 Allow creating desktop part of multi-theme. 2019-09-06 19:31:07 +03:00
John Preston
79106e0c01 Correctly show custom themes in list. 2019-09-06 19:31:07 +03:00
John Preston
7485f0c960 Don't change real theme when editing. 2019-09-06 19:31:07 +03:00
John Preston
03bdd80b2f Open theme editor for existing themes. 2019-09-06 19:31:07 +03:00
John Preston
dd74f57a66 Display full themes list in Settings. 2019-09-06 19:31:07 +03:00
John Preston
534772722e Improve theme selector design. 2019-09-06 19:31:07 +03:00
John Preston
6343221d7b Remove many includes from stdafx.pch. 2019-09-06 19:31:07 +03:00
John Preston
4929de2bfb Apply cloud themes. 2019-09-06 19:31:06 +03:00
John Preston
ac8f924909 Show tdesktop cloud themes in a box. 2019-09-06 19:31:06 +03:00
John Preston
95afcbb485 Format a valid theme file name. 2019-09-06 19:31:06 +03:00
John Preston
229bc56cc8 Upload saved theme to the cloud. 2019-09-06 19:31:06 +03:00
John Preston
4b045a602c Add a box for new theme creating. 2019-09-06 19:31:06 +03:00
John Preston
1e3b72ab74 Ignore background when checking which theme is used. 2019-09-06 19:31:06 +03:00
John Preston
bc63d9fe53 Fix check display in background box. 2019-09-06 19:31:06 +03:00
John Preston
d7cb8b7065 Version 1.8.4: Fix crash in local messages. 2019-09-06 16:41:43 +03:00
John Preston
30f4d870c5 Version 1.8.4: Fix crash on failed message delete. 2019-09-06 14:46:09 +03:00
John Preston
ad515c6f4a Version 1.8.4.
- Bug fixes and other minor improvements.
2019-09-06 14:39:04 +03:00
John Preston
056949416d Apply some rlottie fixes. 2019-09-06 14:37:41 +03:00
John Preston
ab6d9ff73c Fix sending first message in chat. 2019-09-06 14:14:04 +03:00
John Preston
8b766dda8e Fix crash in scheduled message in admin log.
Fixes #6495.
2019-09-06 12:20:59 +03:00
John Preston
ee0f66d746 Fix crash in history clear while sending messages.
Don't remove messages that are currently being sent from locals.
2019-09-06 10:56:01 +03:00
John Preston
7893ad0558 Mark all types of local history items. 2019-09-06 10:53:44 +03:00
John Preston
b0c5a75fb9 Version 1.8.3.
- Right click the 'Send' button and select 'Schedule Message'
to automatically send something at a specified time.
- Schedule reminders for yourself in the 'Saved Messages' chat.
- Get a notification when any of your scheduled messages are sent.
- Customize your app's appearance by choosing accent colors
for the 'Day', 'Night' and 'Tinted' themes.
- Choose who can find you on Telegram
when they add your number to their phone contacts.
- Send a single 😁, 😧, 😡, :poo:, 😢 or
😮 to check out what's new in the animated emoji department.
2019-09-05 21:49:20 +03:00
John Preston
1c313da888 Add dummy handler for addtheme links. 2019-09-05 21:49:20 +03:00
John Preston
ee210ea701 Update API scheme. 2019-09-05 20:42:45 +03:00
John Preston
e0c0d79be9 Fix build for GCC. 2019-09-02 15:22:01 +03:00
John Preston
cb3bad31fa Put 'Classic' theme first. 2019-09-02 14:27:22 +03:00
John Preston
917696be36 Closed alpha version 1.8.2.3. 2019-09-02 13:43:37 +03:00
John Preston
253816641c Submit by keyboard in scheduled section. 2019-09-02 13:39:43 +03:00
John Preston
ca2692473d Fix inactive window notifications clearing. 2019-09-02 13:39:43 +03:00
John Preston
8000dfac01 Fix crash on admin log exit by Escape. 2019-09-02 13:39:43 +03:00
John Preston
85cca51154 Make accent color radiobutton animated. 2019-09-02 13:39:43 +03:00
John Preston
87ea49e094 Allow schedule messages while in slowmode counter. 2019-09-02 13:39:43 +03:00
John Preston
66bf48e21e Remove 'Send without sound' in Saved messages. 2019-08-31 14:43:19 +03:00
John Preston
451056d2ab Track reminder unread status without cheating. 2019-08-31 14:32:15 +03:00
John Preston
791ae64a90 Fix crash in local messages tracking. 2019-08-31 14:25:24 +03:00
John Preston
3b4563772e Disable edit self restrictions in UI. 2019-08-31 14:25:02 +03:00
John Preston
16b786186b Fix schedule time selection. 2019-08-31 14:21:52 +03:00
John Preston
7be1c4ca2f Fix crash in right click on CalendarBox.
Any attempt to destroy a widget on right click will crash,
because Qt anyway sends QContextMenuEvent to the same widget.

Fixes #6464.
2019-08-31 14:15:30 +03:00
John Preston
17fba16c23 Fix crash on quit without initial passcode unlock. 2019-08-30 23:30:12 +03:00
John Preston
5fc4dcd172 Closed alpha version 1.8.2.2. 2019-08-30 18:45:07 +03:00
John Preston
b27a2cd34a Use edit_hide flag to hide 'edited' badge. 2019-08-30 18:00:32 +03:00
John Preston
569340c7d3 Move transfer ownership button. 2019-08-30 17:06:37 +03:00
John Preston
3715fa4b1e Fix scheduled notifications on inbox read. 2019-08-30 17:06:21 +03:00
John Preston
d95e54cb1a Check if it is possible to 'Send now.' 2019-08-30 16:17:46 +03:00
John Preston
470b67f557 Refresh file reference from scheduled messages. 2019-08-30 15:42:01 +03:00
John Preston
c46bcef9ff Add 'Send now' confirmation to langpack. 2019-08-30 15:24:42 +03:00
John Preston
c31cda0587 Use better accent color suggestions. 2019-08-30 13:32:15 +03:00
John Preston
5758f756c9 Fix draft clearing on message send.
Regression was introduced in caef7dde24.
2019-08-30 13:31:45 +03:00
John Preston
f199205592 Limit scheduled requests for one per minute. 2019-08-29 20:16:37 +03:00
John Preston
2b656f7745 Closed alpha version 1.8.2.1. 2019-08-29 16:16:22 +03:00
23rd
7be286751b Fixed text width in toast widget.
- Fixed #6381.
2019-08-29 16:16:22 +03:00
23rd
04617e4a12 Fixed display of tooltip for date of stickers.
- Fixed #6426.
 - Regression was introduced in b814c6307a.
2019-08-29 16:16:22 +03:00
John Preston
405ccb8580 Add editing of AddedByPhone privacy. 2019-08-29 11:44:03 +03:00
John Preston
131ef4f15a Fix palette generation on macOS. 2019-08-29 10:01:28 +03:00
John Preston
07f45b7eab Improve published scheduled notifications. 2019-08-29 10:01:28 +03:00
John Preston
87addd41b1 Add new phrases for reminders. 2019-08-29 10:01:28 +03:00
John Preston
0a4f91a53d Notify about published scheduled messages. 2019-08-29 10:01:28 +03:00
John Preston
3b76a908a4 Try to fix a crash in QNetworkReplyHttpImpl. 2019-08-29 10:01:28 +03:00
John Preston
9e3bc966c8 Fix crash in window destruction. 2019-08-29 10:01:28 +03:00
John Preston
149c69809d Apply peer restrictions. 2019-08-29 10:01:28 +03:00
John Preston
97a239a8b4 Show full emoji in fields in all scales. 2019-08-29 10:01:28 +03:00
John Preston
bd7cee2252 Improve accent color choose design. 2019-08-29 10:01:28 +03:00
John Preston
66d0d6e8fe Use HSV color space for colorizing. 2019-08-29 10:01:28 +03:00
John Preston
117d6192fa Ensure contrast in colorized themes. 2019-08-29 10:01:28 +03:00
John Preston
763bdf8798 Apply lightness limits in accent colors. 2019-08-29 10:01:28 +03:00
John Preston
56a82600f8 Add HSL color picker box for theming. 2019-08-29 10:01:28 +03:00
John Preston
a3e993253c Keep colorized theme in editor. 2019-08-29 10:01:28 +03:00
John Preston
04d5158ae3 Remove unused LeftOutlineButton type. 2019-08-29 10:01:28 +03:00
John Preston
38e4daacd4 Use HSL color space for accent colors. 2019-08-29 10:01:28 +03:00
John Preston
44d156760e Fix build in Xcode. 2019-08-27 19:10:37 +03:00
John Preston
d66541989e Improve first socket message. 2019-08-27 19:10:37 +03:00
John Preston
56c4d164f3 Fix crash in group creation. 2019-08-27 19:10:37 +03:00
John Preston
e2c1c4c8de Fix jump to first chat with archive. 2019-08-27 19:10:37 +03:00
John Preston
a465117689 Allow choosing accent colors from palette. 2019-08-27 19:10:37 +03:00
John Preston
c2117e7722 Save embedded themes accent colors. 2019-08-27 19:10:37 +03:00
John Preston
7de28fc4bd Apply saturation and value in colorizer. 2019-08-27 19:10:37 +03:00
John Preston
529ef64257 Sort colors by hue distance. 2019-08-27 19:10:37 +03:00
John Preston
9cb5423d40 Allow changing accent color in default themes. 2019-08-27 19:10:37 +03:00
John Preston
dd136350fb Fix save color in theme editor. 2019-08-27 19:10:37 +03:00
John Preston
ef7087348a Allow to schedule created polls. 2019-08-27 19:10:37 +03:00
John Preston
8eac2dcb78 Correctly show scheduled polls. 2019-08-27 19:10:37 +03:00
John Preston
f690f93f32 Allow schedule of files, stickers, GIFs. 2019-08-27 19:10:37 +03:00
John Preston
77ebdd3576 Send from scheduled messages section. 2019-08-27 19:10:37 +03:00
John Preston
3e895d0e85 Make tabbed selector working in scheduled section. 2019-08-27 19:10:37 +03:00
John Preston
385a7eb00d Start scheduled compose controls. 2019-08-27 19:10:37 +03:00
John Preston
1c9775baf9 Improve scheduled messages icon design. 2019-08-27 19:10:37 +03:00
John Preston
fb96d2eef8 Improve schedule box design. 2019-08-27 19:10:37 +03:00
John Preston
debeb61540 Start schedule message box. 2019-08-27 19:10:37 +03:00
John Preston
03cdddfe18 Allow sending scheduled messages. 2019-08-27 19:10:37 +03:00
John Preston
caef7dde24 Bundle silent and scheduled to Api::SendOptions. 2019-08-27 19:10:37 +03:00
John Preston
0b08810d5a Update hash counting for scheduled messages. 2019-08-27 19:10:37 +03:00
John Preston
694f771131 Allow to send scheduled messages instantly. 2019-08-27 19:10:37 +03:00
John Preston
956bb876f6 Sort scheduled messages on an update. 2019-08-27 19:10:37 +03:00
John Preston
99037d3d46 Improve scheduled messages top bar. 2019-08-27 19:10:37 +03:00
John Preston
ea0a616453 Allow deleting scheduled messages. 2019-08-27 19:10:37 +03:00
John Preston
815a18be94 Add initial scheduled list implementation. 2019-08-27 19:10:37 +03:00
John Preston
3814b0833d Keep track of scheduled messages. 2019-08-27 19:10:37 +03:00
John Preston
549789bfb7 Update API scheme to layer 105. 2019-08-27 19:10:37 +03:00
John Preston
a539fad3e2 Version 1.8.2: Fix passcode unlock. 2019-08-20 16:03:21 +03:00
John Preston
ee96d78656 Version 1.8.2: Fix build for Qt 5.3.2. 2019-08-20 14:29:21 +03:00
John Preston
288c1130b9 Version 1.8.2: Fix build. 2019-08-20 14:28:40 +03:00
John Preston
d1083a1fb4 Version 1.8.2.
- Bug fixes and other minor improvements.
2019-08-20 12:49:51 +03:00
John Preston
e1fe373504 Fix localtime-related slowmode errors. 2019-08-20 12:42:37 +03:00
23rd
1a06714f3a Added ability to apply formatting from Mac menu. 2019-08-20 12:02:37 +03:00
John Preston
7316d24ca4 Quick fix for 'Cant forward here' error. 2019-08-19 15:26:12 +03:00
John Preston
b814c6307a Check that sticker drag point is inside. 2019-08-19 15:26:12 +03:00
23rd
06fbb2edc4 Added permanent date display for sticker when fast action is enabled. 2019-08-19 15:26:12 +03:00
23rd
0ee47bb10f Fixed fast action button and date position for stickers. 2019-08-19 15:26:12 +03:00
23rd
9f228d8146 Fixed fast share button in messages with big emoji and reply. 2019-08-19 15:26:12 +03:00
23rd
de270d5283 Fixed right action click handler on stickers without stickerpack. 2019-08-19 15:26:12 +03:00
John Preston
3f041545bf Fix crash in local passcode reset. 2019-08-19 15:26:12 +03:00
John Preston
1139a59818 Fix crash in editing message to empty. 2019-08-19 15:26:12 +03:00
John Preston
1e64ba8f29 Fix app config refresh timeout. 2019-08-19 15:26:12 +03:00
John Preston
65342559c7 Use specific tag for libxkbcommon in docs. 2019-08-19 15:23:37 +03:00
John Preston
e150db1d0b Use specific tag for libkxbcommon in snap build. 2019-08-19 13:34:24 +03:00
John Preston
4548b14b45 Fix libvdpau repository link in snap build. 2019-08-19 13:15:32 +03:00
John Preston
dac1128dc9 Version 1.8.1.
- Bug fixes and other minor improvements.
2019-08-09 20:36:21 +01:00
John Preston
c3944d95b4 Fix local caching.
Regression was introduced in 9cf4cf6dca.

Fixes #6377.
2019-08-09 20:33:05 +01:00
John Preston
02b65a42f7 Move message client flags to a separate field. 2019-08-09 20:19:23 +01:00
362 changed files with 15244 additions and 4438 deletions

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -131,6 +131,9 @@ private:
AdminDoneCallback _adminDoneCallback;
BannedDoneCallback _bannedDoneCallback;
protected:
bool _excludeSelf = true;
};
// Finds chat/channel members, then contacts, then global search results.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 &params) {
@@ -104,9 +67,7 @@ QRect TabbedSection::rectForFloatPlayer() const {
TabbedSection::~TabbedSection() {
beforeHiding();
if (_returnMethod) {
_returnMethod(takeSelector());
}
controller()->takeTabbedSelectorOwnershipFrom(this);
}
} // namespace ChatHelpers

View File

@@ -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 &params) override;
@@ -73,8 +57,7 @@ protected:
}
private:
object_ptr<TabbedSelector> _selector;
Fn<void(object_ptr<TabbedSelector>)> _returnMethod;
const not_null<TabbedSelector*> _selector;
};

View File

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

View File

@@ -70,7 +70,6 @@ public:
void setRoundRadius(int radius);
void refreshStickers();
void showMegagroupSet(ChannelData *megagroup);
void setCurrentPeer(PeerData *peer);
void hideFinished();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View File

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

View File

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

View File

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

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