Compare commits

..

236 Commits

Author SHA1 Message Date
John Preston
f3a2460a54 Version 2.4.1: Fix build for Linux. 2020-10-01 20:06:00 +03:00
John Preston
04212140cc Version 2.4.1: Fix capture error reporting. 2020-10-01 18:46:22 +03:00
John Preston
4e1904b137 Version 2.4.1.
- Move by PageUp and PageDown in channel comments.
- Several layout bugfixes.
- Several crashfixes.
2020-10-01 18:24:29 +03:00
John Preston
0299ba4873 Allow in groups to delete service messages for everyone.
Fixes #8690.
2020-10-01 18:21:39 +03:00
23rd
46ce0df832 Fixed crash in SessionsBox when list of sessions is empty. 2020-10-01 18:14:09 +03:00
John Preston
d66debd802 Fix crash on bad message in Replies section. 2020-10-01 18:08:27 +03:00
John Preston
454fe8cdf7 Fix crash in calls box. 2020-10-01 17:52:02 +03:00
John Preston
c4dfc634d0 Fix crash in main window destruction. 2020-10-01 17:45:25 +03:00
John Preston
b08fa069b4 Fix assertion violation in case of bad messages. 2020-10-01 17:38:49 +03:00
John Preston
3d20958bb4 Remove assertion about taskbar position. 2020-10-01 17:33:59 +03:00
John Preston
c693fcb2b0 Reopen third column in Replies section.
Fixes #8674, fixes #8687.
2020-10-01 17:20:08 +03:00
John Preston
ddad42d80e Add report button to comments context menu.
Fixes #8679.
2020-10-01 17:06:04 +03:00
John Preston
c6f66e83ee Fix restriction label display.
Fixes #8680.
2020-10-01 16:53:39 +03:00
John Preston
8bb3b7fada Handle some errors on comments open.
Fixes #8682.
2020-10-01 16:42:31 +03:00
Ilya Fedin
1d24d29afa Little cleanup for Linux platform code & build
CheckCXXSourceCompiles is not needed anymore

Material wayland decorations could be checked just with IsQtPluginsBundled
2020-10-01 16:34:01 +03:00
Ilya Fedin
0536a479f9 Use startSystemMove/startSystemResize instead of platform code on Wayland with Qt 5.15 2020-10-01 16:30:53 +03:00
Ilya Fedin
7fef7e6315 Don't add shadow on Wayland
It was implemented like a hack and worked like a hack... Looks like it is better to wait until Qt give a way to create shadows.
2020-10-01 16:30:53 +03:00
John Preston
c6ef2b057e Fix couple of visual glitches.
Fixes #8676.
2020-10-01 16:29:09 +03:00
John Preston
784f10678c Fix root comments post layout. 2020-10-01 15:12:57 +03:00
John Preston
92dbd7089b Fix comments layout bug for narrow photos. 2020-10-01 14:05:26 +03:00
John Preston
415990c913 Show View thread button only in discussions for now. 2020-10-01 13:38:36 +03:00
John Preston
e42af74dd2 Don't try to open comments in invite peek channel. 2020-10-01 13:15:10 +03:00
John Preston
bd1a46252d Show admin rank for anonymous posts. 2020-10-01 12:57:03 +03:00
John Preston
874e5e0a61 Fix export of discussion messages. 2020-10-01 12:42:35 +03:00
John Preston
81457693f1 Don't add comments button for inline markup messages.
Fixes #8664.
2020-10-01 11:44:08 +03:00
John Preston
3a700650be Remove comments info if no info in server data. 2020-10-01 11:34:59 +03:00
John Preston
d642c3f3b5 Hide bot about header for Replies chat. 2020-10-01 11:19:14 +03:00
John Preston
4be03ffc25 Fix PageUp/PageDown scrolling in Replies section.
Fixes #8666.
2020-10-01 11:00:29 +03:00
Ilya Fedin
dcac3146c7 Fix Linux GitHub action 2020-10-01 10:54:42 +03:00
Ilya Fedin
10012d6b31 Handle launcher basename compile-time for snap
Just like for flatpak
2020-10-01 10:54:42 +03:00
John Preston
3aa1b1e9ae Fix recent commenters userpics border. 2020-10-01 10:52:53 +03:00
John Preston
4e8a1f8d29 Fix voice messages sending. 2020-10-01 10:47:03 +03:00
John Preston
11e03a181d Version 2.4: Fix crash in typing animations. 2020-09-30 21:11:24 +03:00
John Preston
4e366dc86e Fix widget order in compose controls. 2020-09-30 21:11:24 +03:00
23rd
015277c4d3 Disabled top moving of dialogs when window has normal adaptive layout.
Fixed #8370.
2020-09-30 21:11:24 +03:00
John Preston
745bbfe268 Fix fast reply from message corner in Replies section. 2020-09-30 21:11:24 +03:00
John Preston
e8a58c4c8d Fix crash in replies list management. 2020-09-30 21:11:24 +03:00
John Preston
d4f2c96322 Version 2.4: Fix build on macOS. 2020-09-30 19:58:30 +03:00
John Preston
bd1d0417a9 Version 2.4: Fix submodules. 2020-09-30 19:20:08 +03:00
John Preston
4a78eb100a Version 2.4.
- Turn on "Remain Anonymous" in an admin's Permissions to let them post
on behalf of the group and become invisible in the list of members.
- Comment on posts in channels that have a discussion group.
- Get notified about replies to your comments via the new Replies chat
(if you are not a member of the discussion group).
2020-09-30 19:15:43 +03:00
Ilya Fedin
7fa5ca192b Fix showing autohidden taskbar with maximized window 2020-09-30 19:14:51 +03:00
John Preston
371510cfe2 Fix layout bug in box title with right button. 2020-09-30 19:01:54 +03:00
23rd
622c1a910b Moved text changes of QAction in peer menu to single place. 2020-09-30 19:00:53 +03:00
23rd
ce3279143d Connected popup menu display to archive button display in main menu. 2020-09-30 19:00:53 +03:00
23rd
3e6ba53a04 Reduced number of requests for authorizations list in SessionsBox. 2020-09-30 18:48:35 +03:00
23rd
41cb37b091 Added labels update in privacy box by timer. 2020-09-30 18:48:35 +03:00
23rd
264dd0c1d2 Refactored row painting in sessions box. 2020-09-30 18:48:34 +03:00
23rd
163e549708 Added count of active sessions as label of button in settings. 2020-09-30 18:48:34 +03:00
23rd
035a19b41e Removed authorizations check form Data::Session. 2020-09-30 18:48:34 +03:00
23rd
6ac5f32796 Moved other authorizations api request to separated file. 2020-09-30 18:48:34 +03:00
23rd
0c4c4b2fcf Added ability to move content of SessionsBox to Section. 2020-09-30 18:48:34 +03:00
23rd
a106d6e804 Moved content of SessionsBox to separate inner class. 2020-09-30 18:48:34 +03:00
23rd
6b7c33f0ee Moved same parts of code of terminating in SessionsBox to single place. 2020-09-30 18:48:34 +03:00
23rd
8d2cacac80 Replaced variable of loading in SessionsBox with rpl::variable. 2020-09-30 18:48:34 +03:00
John Preston
cc9eb7f893 Support write restrictions in Replies section. 2020-09-30 18:38:58 +03:00
John Preston
94c2969f8b Support slowmode restrictions in Replies section. 2020-09-30 16:29:32 +03:00
John Preston
719bed6e85 Move some text formatting to tg_ui:ui/text/format_values. 2020-09-30 16:29:17 +03:00
John Preston
8634c1f7f3 Move lang keys to td_lang library. 2020-09-30 12:18:52 +03:00
John Preston
def1266216 Rename td-specific libs from lib_ to td_. 2020-09-30 10:51:17 +03:00
23rd
2e02f27a5c Fixed tabbed selector display in Replies / Scheduled messages sections. 2020-09-29 23:44:03 +03:00
John Preston
04855f1697 Fix instructions for Qt build without ICU. 2020-09-29 23:43:22 +03:00
John Preston
c29730650e Closed alpha version 2.3.2.2. 2020-09-29 20:20:31 +03:00
John Preston
6257445d5e Fix build on Xcode 12. 2020-09-29 20:20:21 +03:00
John Preston
405c8125da Allow sending voice messages in Replies / Scheduled. 2020-09-29 19:03:30 +03:00
John Preston
62da24c20b Improve scroll to unread bar. 2020-09-29 19:03:30 +03:00
John Preston
e8df47c926 Show send action animations in Replies thread. 2020-09-29 19:03:30 +03:00
John Preston
433c147dd0 Show message from Replies bot inside thread. 2020-09-29 19:03:30 +03:00
John Preston
8f4fdb6d0d Fix crash in mark folder as read. 2020-09-29 19:03:30 +03:00
23rd
aecdc01e41 Added ability to mark all chats as read. 2020-09-29 19:03:29 +03:00
23rd
bdce2d5e25 Added ability to mark as read chats from folder from context menu.
Fixed #7507.
Fixed #6004.
2020-09-29 19:03:29 +03:00
23rd
5968219fe4 Added ability to mark as read selected chat with shortcut. 2020-09-29 19:03:29 +03:00
23rd
f81271d1fe Simplified confirmed callback for ConfirmBox instances. 2020-09-29 19:03:29 +03:00
23rd
9f3af7234e Added ability to pass confirm callback with close to ConfirmBox. 2020-09-29 19:03:29 +03:00
John Preston
90c0929407 Load Rstrtmgr dynamically. 2020-09-29 19:03:29 +03:00
John Preston
1e31cda78d Fix root comments post layout. 2020-09-29 19:03:29 +03:00
John Preston
f7e4f18e9b Fix a crash in failed-sent scheduled messages. 2020-09-29 19:03:29 +03:00
John Preston
0fb42ed82a Use special method for blocking from Replies. 2020-09-29 19:03:29 +03:00
John Preston
c535a7c564 Use MaskedInputField for URL input. 2020-09-29 19:03:29 +03:00
John Preston
c32f2e71e8 Update fast action icons. 2020-09-29 19:03:28 +03:00
John Preston
a38f7b357c Closed alpha version 2.3.2.1. 2020-09-29 19:03:28 +03:00
John Preston
438f69e1b2 Improve root comments message layout. 2020-09-29 19:03:28 +03:00
John Preston
891b4a91a3 Generate correct links to replies section. 2020-09-29 19:03:28 +03:00
John Preston
889139f31f Handle links to replies section. 2020-09-29 19:03:28 +03:00
John Preston
247b1f64ca Show correct outbox ticks in replies section. 2020-09-29 19:03:27 +03:00
John Preston
13ad590a51 Track inbox/outbox comments/replies read state. 2020-09-29 19:03:27 +03:00
John Preston
e021e0beb3 Resize scroll area together with pinned root message. 2020-09-29 19:03:27 +03:00
John Preston
b097bd7225 Report spam from Replies reports messages. 2020-09-29 19:03:27 +03:00
John Preston
27f85df562 Click on root pinned bar throws to replies start. 2020-09-29 19:03:27 +03:00
John Preston
e484bc78d0 Update API scheme on layer 119. 2020-09-29 19:03:27 +03:00
John Preston
d89be1d1d4 Don't read messages in channels I'm not in. 2020-09-29 19:03:27 +03:00
John Preston
ab429212e5 Respect global group read position in replies. 2020-09-29 19:03:27 +03:00
John Preston
f53f934001 Handle new typing update in channels. 2020-09-29 19:03:27 +03:00
John Preston
a287dec242 Allow sending typing/send actions for Replies section. 2020-09-29 19:03:27 +03:00
John Preston
f73b0f0b0d Update API scheme on layer 119. 2020-09-29 19:03:27 +03:00
John Preston
61d89113d4 Handle correctly comment links for public channels. 2020-09-29 19:03:26 +03:00
John Preston
7862443fcb Show comments button for unwrapped medias. 2020-09-29 19:03:26 +03:00
John Preston
1a40f2b3ef Fix userpics display for min-loaded users. 2020-09-29 19:03:26 +03:00
John Preston
4c1213ce9e Allow blocking users from Replies chat. 2020-09-29 19:03:26 +03:00
John Preston
afbc0c498f Use service message instead of unread bar. 2020-09-29 19:03:26 +03:00
John Preston
a91c078fb1 Count attachments only on visible items. 2020-09-29 19:03:26 +03:00
John Preston
6eedeb3852 Inject full album to the Replies section. 2020-09-29 19:03:26 +03:00
John Preston
8af559e711 Use first message of album as the leader. 2020-09-29 19:03:26 +03:00
John Preston
7f928a92ea Show root as pinned when not visible as message. 2020-09-29 19:03:26 +03:00
John Preston
22dc7601f5 Fix root message pinned bar shadow. 2020-09-29 19:03:26 +03:00
John Preston
9abca29f4c Show comments bar when no unread bar. 2020-09-29 19:03:25 +03:00
John Preston
cf48152853 Inject discussed message when no comments. 2020-09-29 19:03:25 +03:00
John Preston
883c3ecf65 Improve root message layout in Replies section. 2020-09-29 19:03:25 +03:00
John Preston
9a96298ef7 Disable revoke all history for bots. 2020-09-29 19:03:25 +03:00
John Preston
a1a845dbf1 Improve Replies chat design. 2020-09-29 19:03:25 +03:00
John Preston
40e925d3f9 Remove redundant read comments thread requests. 2020-09-29 19:03:25 +03:00
John Preston
808e8dcf4f Inject discussed message in Replies section. 2020-09-29 19:03:25 +03:00
John Preston
c2bb2526d3 Handle updateReadDiscussion. 2020-09-29 19:03:25 +03:00
John Preston
59abe95754 Show comments unread status. 2020-09-29 19:03:25 +03:00
John Preston
040f29abe6 Remove some options from Replies three-dot menu. 2020-09-29 19:03:25 +03:00
John Preston
e9dffe78e3 Fix minimal width of message bubble with comments. 2020-09-29 19:03:25 +03:00
John Preston
3a51303fb0 Add go to original button to Replies chat. 2020-09-29 19:03:25 +03:00
John Preston
008a301755 Paint some sort of replies icon. 2020-09-29 19:03:24 +03:00
John Preston
be14456290 Show correct reply in Replies bot. 2020-09-29 19:03:24 +03:00
John Preston
4a94a0c438 Show replies dialog correctly. 2020-09-29 19:03:24 +03:00
John Preston
608d8307d9 Scroll to bottom when sending a comment. 2020-09-29 19:03:24 +03:00
John Preston
6f9ea1cc01 Allow posting comments being not in group. 2020-09-29 19:03:24 +03:00
John Preston
47170da813 Show comments button using 'has_link' flag. 2020-09-29 19:03:24 +03:00
John Preston
01ab6e6d4d Support anonymous group admins. 2020-09-29 19:03:24 +03:00
John Preston
b8424b1d89 Add read status tracking to comments. 2020-09-29 19:03:24 +03:00
John Preston
78d83a2c69 Show local sending messages in replies section. 2020-09-29 19:03:24 +03:00
John Preston
beb623bee2 Add root message display in replies section. 2020-09-29 19:03:23 +03:00
John Preston
d42ce87c09 Move discussion group link to three-dot menu. 2020-09-29 19:03:23 +03:00
John Preston
60002555c3 Track comments count correctly. 2020-09-29 19:03:23 +03:00
John Preston
fb20be3e6c Open channel comments, not replies. 2020-09-29 19:03:23 +03:00
John Preston
31e1ed216a Add comments button to channel posts. 2020-09-29 19:03:23 +03:00
John Preston
ce91caa820 Fix build on macOS. 2020-09-29 19:03:23 +03:00
John Preston
95a579f25f Clear text box when sending media in replies. 2020-09-29 19:03:23 +03:00
John Preston
9fe82480e1 Reply in replies by double click. 2020-09-29 19:03:23 +03:00
John Preston
17549ad5ea Allow replying inside replies section. 2020-09-29 19:03:23 +03:00
John Preston
f22a804220 Add navigation through reply stack. 2020-09-29 19:03:22 +03:00
John Preston
c563df7d9d Fix navigation in a message replies section. 2020-09-29 19:03:22 +03:00
John Preston
1849f01b15 Jump to near reply inside a replies thread. 2020-09-29 19:03:22 +03:00
John Preston
f8b83dd186 Remove mapbox::variant dependency. 2020-09-29 19:03:22 +03:00
John Preston
f0e1d2fd02 'base::optional_variant<' -> 'std::variant<v::null_t,' 2020-09-29 19:03:19 +03:00
John Preston
734d834a20 Replace mapbox variant with std::variant. 2020-09-29 19:03:19 +03:00
John Preston
b3eb41b989 Fix build instructions for old OS X versions. 2020-09-29 19:03:19 +03:00
John Preston
45419205c6 Simple sending from replies section. 2020-09-29 19:03:19 +03:00
John Preston
204645a715 Stick to bottom on new messages. 2020-09-29 19:03:19 +03:00
John Preston
50a0429786 Hide root reply info in replies thread. 2020-09-29 19:03:19 +03:00
John Preston
00cdae0369 Implement replies list request. 2020-09-29 19:03:18 +03:00
John Preston
437c9320cd Implement progressive jpeg loading and sending. 2020-09-29 19:03:18 +03:00
John Preston
0888901d79 Try using progressive jpeg photo size. 2020-09-29 19:03:18 +03:00
John Preston
55edb3bdfe Update API scheme to layer 119. Count replies. 2020-09-29 19:03:18 +03:00
John Preston
fcdc4cd465 Handle bot callback buttons with password. 2020-09-29 19:03:18 +03:00
John Preston
49c230b898 Update API scheme to layer 118. 2020-09-29 19:03:18 +03:00
John Preston
883a62c0a2 Return 'testmode' code when no authed accounts. 2020-09-29 19:03:18 +03:00
23rd
58008ab7b0 Removed App::wnd from local_url_handlers. 2020-09-29 19:02:52 +03:00
23rd
94468ecf6d Added ability to copy link of not installed sticker pack. 2020-09-29 19:02:52 +03:00
23rd
91118bf087 Removed App::wnd from history_view_context_menu. 2020-09-29 19:02:52 +03:00
Ilya Fedin
6805085bbc Use unique desktop file name for static binary 2020-09-29 18:54:01 +03:00
Ilya Fedin
78d874e9a3 Disable OpenAL WASAPI backend 2020-09-29 18:28:42 +03:00
BugLight
8d5e356733 Add custom host input that replaces commas with dots 2020-09-29 18:27:36 +03:00
Ilya Fedin
4c2779bbaf Fix saving maximized state on Linux 2020-09-29 18:25:57 +03:00
Ilya Fedin
70c993774a Get rid of redudant cmake options 2020-09-29 18:24:16 +03:00
Ilya Fedin
f128665f6b Fix -debug argument
processArguments is called before ComputeDebugMode gets called, Logs::DebugEnabled gets overriden that way.
Fix that by setting gDebugMode variable and checking it in ComputeDebugMode.
2020-09-20 16:51:01 +03:00
Ilya Fedin
0cd68f866d Get icon theme from gtk if qt5ct is unconfigured 2020-09-20 16:51:01 +03:00
Ilya Fedin
242ced4022 Write icon theme names to debug log 2020-09-20 16:51:01 +03:00
Vitaly Kryukov
23aef6c365 Update mtproto_domain_resolver.cpp 2020-09-10 19:46:48 +03:00
Vitaly Kryukov
210e3f0cb6 Bump DNS User-Agent 2020-09-10 19:46:48 +03:00
Ilya Fedin
d86b4659d6 Create OpenAL context without attributes 2020-09-10 19:45:43 +03:00
Ilya Fedin
f1cf6b4896 Move snap action to ubuntu-latest since it uses lxd 2020-09-10 19:39:52 +03:00
Ilya Fedin
8fd1253266 GNOME Wayland doesn't support native decorations 2020-09-10 19:39:21 +03:00
Ilya Fedin
5991cd4350 Add missed no-tests in release openssl build for Windows 2020-09-10 19:38:23 +03:00
Ilya Fedin
309372164c Cache make install calls in macos action 2020-09-10 19:38:23 +03:00
John Preston
f1cdc7e3f9 Update qt5ct submodule. 2020-08-31 17:40:03 +04:00
Ilya Fedin
dfad68a0b8 Fix tg_owt url 2020-08-31 12:01:43 +04:00
Ilya Fedin
0f887b3432 Update submodules 2020-08-31 12:01:43 +04:00
Ilya Fedin
077f0c393e No need to rebuild ffmpeg without NEON optimizations anymore 2020-08-31 12:01:43 +04:00
Ilya Fedin
7d29f9ce17 Don't check sws_scale return value
To workaround ffmpeg bug: https://gitlab.alpinelinux.org/alpine/aports/-/issues/11722
2020-08-31 12:01:43 +04:00
Ilya Fedin
6635d03818 Check if xcb-screensaver extension present 2020-08-30 17:00:43 +04:00
Ilya Fedin
e523492de0 Add missed methods to specific_mac.h and specific_win.h 2020-08-30 17:00:43 +04:00
Ilya Fedin
c77f1bf082 Add UnsetWindowExtents method 2020-08-30 17:00:43 +04:00
Ilya Fedin
3c8c059447 Eliminate the need of Q_OBJECT in main_window_linux and main_window_win 2020-08-30 17:00:43 +04:00
Ilya Fedin
612e0d4a10 Update window shadow size on window state change 2020-08-30 17:00:43 +04:00
Ilya Fedin
78a2835bbf Extents should be updated on each event only on Wayland 2020-08-30 17:00:43 +04:00
Ilya Fedin
ea8e256a23 Revert "Revert "Add main window shadow for Linux""
This reverts commit b7f5cfe083.
2020-08-30 17:00:43 +04:00
Ilya Fedin
2f2de84b43 Fix d-bus type in MutterDBusLastUserInputTime 2020-08-30 16:57:51 +04:00
John Preston
ead6892857 Update cmake_helpers submodule. 2020-08-30 16:54:47 +04:00
Ilya Fedin
2b642d4da9 -Wno-register is defined in options_linux.cmake now 2020-08-30 16:28:50 +04:00
Ilya Fedin
a474074705 Adapt snap build for tg_owt 2020-08-30 16:28:50 +04:00
John Preston
d34eabdc11 Fix crash in poll view destruction. 2020-08-24 17:59:44 +04:00
John Preston
f9be304e54 Fix crash in logout from passcoded screen. 2020-08-24 16:57:36 +04:00
John Preston
39b0662a2c Fix article inline results with long titles. 2020-08-24 16:34:55 +04:00
John Preston
ae59de7652 Version 2.3.2.
- Revert custom window shadow on Linux.
2020-08-23 17:36:52 +04:00
John Preston
b7f5cfe083 Revert "Add main window shadow for Linux"
This reverts commit d02092f09e.

Conflicts:
	Telegram/SourceFiles/mainwindow.cpp
	Telegram/SourceFiles/platform/linux/specific_linux.cpp
	Telegram/SourceFiles/window/main_window.cpp
2020-08-23 16:38:49 +04:00
23rd
97076dbf83 Replaced origin webrtc with custom-built fork in Github CI.
Updated building docs.
2020-08-21 17:35:32 +03:00
John Preston
7b1c47ff2e Version 2.3.1.
- Fix Calls Settings for Video Calls.
2020-08-21 18:22:53 +04:00
John Preston
21c578cf2e Add some error reporting about camera problems. 2020-08-21 17:52:13 +04:00
John Preston
4d2041ae48 Share video capturer between call and settings. 2020-08-21 17:52:13 +04:00
John Preston
4672e3d068 Improve calls settings (camera / microphone). 2020-08-21 17:52:13 +04:00
John Preston
ade7745b0b Fix controls shadows on call panel. 2020-08-21 17:52:13 +04:00
John Preston
c7881ae4a3 Fix building docs for Windows. 2020-08-21 17:52:13 +04:00
John Preston
ba89242759 Preserve preferred aspect through camera change. 2020-08-21 17:52:13 +04:00
John Preston
0139390c71 Use tg_owt in OS X 10.10-10.11 build. 2020-08-21 17:52:13 +04:00
John Preston
693d3a922f Use webrtc from a custom-built fork. 2020-08-21 17:52:13 +04:00
John Preston
ebf8a20d0d Fix main window drag by title on macOS. 2020-08-21 17:52:13 +04:00
John Preston
41c98a6c49 Fix build on macOS. 2020-08-21 17:52:13 +04:00
John Preston
aa87d627c9 Allow to choose camera device in Settings. 2020-08-21 17:52:13 +04:00
John Preston
e782e065a0 Use audio device enumeration from lib_webrtc. 2020-08-21 17:52:12 +04:00
John Preston
f36240eb38 Check for at least one camera existance. 2020-08-21 17:52:12 +04:00
John Preston
72861a6409 Remove phone number pattern for Germany. 2020-08-21 17:52:12 +04:00
23rd
6e3fb253b9 Added ability to remove recent stickers. 2020-08-21 16:23:44 +03:00
23rd
8a34f29329 Moved toggling of existing media to separate file. 2020-08-19 14:47:25 +03:00
Ilya Fedin
2574389129 Fix snap build on Launchpad 2020-08-19 09:58:24 +04:00
Ilya Fedin
3a66d317ee Use lxd for snapcraft
To get the same build environment as on Launchpad
2020-08-19 09:58:24 +04:00
John Preston
ecdee8812d Update cmake submodules and patches revision. 2020-08-18 18:17:39 +04:00
23rd
0e00bbb012 Updated range-v3 to 0.11.0. 2020-08-18 18:17:31 +04:00
23rd
3d8b691ff9 Added video icon in call row in calls box. 2020-08-18 12:48:26 +03:00
John Preston
57f8b03949 Remove old gtk library linking. 2020-08-18 10:22:51 +04:00
Ilya Fedin
6dfefa3f21 g_message is a more appropriate log level for some messages 2020-08-18 08:16:17 +04:00
Ilya Fedin
eaae7b1d03 Decrease indentation in XCBLastUserInputTime 2020-08-18 08:16:17 +04:00
Ilya Fedin
d02092f09e Add main window shadow for Linux 2020-08-18 08:16:17 +04:00
Ilya Fedin
c529974da5 Add webrtc support for snap 2020-08-18 08:05:37 +04:00
John Preston
0e1f59a0ed Fix crash in editing a deleted message.
Regression was introduced in b02b690747.
2020-08-17 13:50:16 +04:00
John Preston
fe97939abf Fix possible crash in call error handling. 2020-08-17 13:50:16 +04:00
John Preston
8ce798db12 Move incoming video to a separate widget. 2020-08-17 13:49:45 +04:00
John Preston
f801cb822e Revert "Update ffmpeg to 4.3 in snap"
This reverts commit 6c52b4630c.
2020-08-17 10:29:18 +04:00
Ilya Fedin
1dcbb103a8 Move gtk clibpboard to linux_libs 2020-08-15 17:43:28 +04:00
Ilya Fedin
d97880913c Get rid of TDESKTOP_USE_PACKAGED_TGVOIP 2020-08-15 17:43:28 +04:00
Ilya Fedin
47673bba50 Move cExeName checks 2020-08-15 17:43:28 +04:00
Ilya Fedin
580e6baee6 Get rid of redudant fontconfig hack 2020-08-15 17:43:28 +04:00
Ilya Fedin
f56af090e0 Use GAppInfo instead of xdg-mime
This makes registering custom scheme distribution-compatible
2020-08-15 17:43:28 +04:00
Ilya Fedin
95c0c400c7 Use QProcess instead of system()
Use g_get_home_dir since it already uses both $HOME and passwd
2020-08-15 17:43:28 +04:00
Ilya Fedin
170ec16f39 Remove outdated snap autostart hack 2020-08-15 17:43:28 +04:00
Ilya Fedin
786bedf271 Disable Qt's integration mechanism on GTK-based DE to avoid early GTK loading
This also removes TDESKTOP_USE_GTK_FILE_DIALOG since it doesn't forces GTK dialog for anyone and there is no reason to leave it

TDESKTOP_DISABLE_GTK_INTEGRATION can be also tested via action now
2020-08-15 17:43:28 +04:00
Ilya Fedin
1de1747c38 Fix length of some lines in specific_linux and main_window_linux 2020-08-15 17:43:28 +04:00
Ilya Fedin
7bb4e5e4a9 Reorder Linux libraries 2020-08-15 17:43:28 +04:00
Ilya Fedin
5dad293335 Add warnings about some build options and environment variables 2020-08-15 17:43:28 +04:00
Ilya Fedin
e4ac70090e Use GDBus to send notifications to avoid Q_OBJECT 2020-08-15 17:43:28 +04:00
23rd
bc7925985c Added WebRTC building to Github CI. 2020-08-15 00:03:37 +04:00
John Preston
a911f2c0c3 Improve build script. 2020-08-15 00:02:43 +04:00
420 changed files with 13978 additions and 6706 deletions

View File

@@ -54,6 +54,7 @@ jobs:
defines:
- ""
- "DESKTOP_APP_DISABLE_DBUS_INTEGRATION"
- "TDESKTOP_DISABLE_GTK_INTEGRATION"
env:
GIT: "https://github.com"
@@ -95,7 +96,8 @@ jobs:
libgtk2.0-dev libice-dev libsm-dev libicu-dev libdrm-dev dh-autoreconf \
autoconf automake build-essential libxml2-dev libass-dev libfreetype6-dev \
libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev \
libvorbis-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-screensaver0-dev \
libvorbis-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev \
libxcb-screensaver0-dev libjpeg-dev ninja-build \
libxcb-xfixes0-dev libxcb-keysyms1-dev libxcb-icccm4-dev libatspi2.0-dev \
libxcb-render-util0-dev libxcb-util0-dev libxcb-xkb-dev libxrender-dev \
libasound-dev libpulse-dev libxcb-sync0-dev libxcb-randr0-dev libegl1-mesa-dev \
@@ -220,7 +222,6 @@ jobs:
--disable-network \
--disable-autodetect \
--disable-everything \
--disable-neon \
--disable-alsa \
--disable-iconv \
--enable-libopus \
@@ -373,7 +374,7 @@ jobs:
make -j$(nproc)
sudo make DESTDIR="$LibrariesPath/openssl-cache" install_sw
cd ..
rm -rf $opensslDir
# rm -rf $opensslDir # Keep this folder for WebRTC.
- name: OpenSSL install.
run: |
cd $LibrariesPath
@@ -442,6 +443,7 @@ jobs:
-qt-harfbuzz \
-qt-pcre \
-qt-xcb \
-no-icu \
-no-gtk \
-static \
-dbus-runtime \
@@ -512,6 +514,39 @@ jobs:
mkdir -p breakpad/out/Default/
cp breakpad-cache/dump_syms breakpad/out/Default/dump_syms
- name: WebRTC cache.
id: cache-webrtc
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/tg_owt
key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}
- name: WebRTC.
if: steps.cache-webrtc.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
git clone $GIT/desktop-app/tg_owt.git
mkdir -p tg_owt/out/Debug
cd tg_owt/out/Debug
cmake -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DTG_OWT_SPECIAL_TARGET=linux \
-DTG_OWT_LIBJPEG_INCLUDE_PATH=`pwd`/../../../qt_$QT/qtbase/src/3rdparty/libjpeg \
-DTG_OWT_OPENSSL_INCLUDE_PATH=$OPENSSL_PREFIX/include \
-DTG_OWT_OPUS_INCLUDE_PATH=/usr/local/include/opus \
-DTG_OWT_FFMPEG_INCLUDE_PATH=/usr/local/include \
../..
ninja
# Cleanup.
cd $LibrariesPath/tg_owt
mv out/Debug/libtg_owt.a libtg_owt.a
rm -rf out
mkdir -p out/Debug
mv libtg_owt.a out/Debug/libtg_owt.a
rm -rf $LibrariesPath/openssl_${OPENSSL_VER}
- name: Telegram Desktop build.
if: env.ONLY_CACHE == 'false'
run: |

View File

@@ -154,7 +154,7 @@ jobs:
$MIN_MAC
make build_libs -j$(nproc)
SSL_DIR=$LibrariesPath/openssl_${{ env.OPENSSL_VER }}
SSL_DIR=$LibrariesPath/openssl_$OPENSSL_VER
mkdir -p $SSL_DIR/include
copyLib() {
cp $1.a $SSL_DIR/$1.a
@@ -167,29 +167,33 @@ jobs:
id: cache-opus
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/opus
path: ${{ env.LibrariesPath }}/opus-cache
key: ${{ runner.OS }}-opus-${{ env.CACHE_KEY }}
- name: Opus.
- name: Opus clone.
run: |
cd $LibrariesPath
git clone -b v1.3 --depth=1 $GIT/xiph/opus
- name: Opus build.
if: steps.cache-opus.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
git clone $GIT/xiph/opus
cd opus
git checkout v1.3
./autogen.sh
CFLAGS="$MIN_MAC $UNGUARDED" CPPFLAGS="$MIN_MAC $UNGUARDED" LDFLAGS="$MIN_MAC" ./configure --prefix=$PREFIX
make -j$(nproc)
sudo make DESTDIR="$LibrariesPath/opus-cache" install
- name: Opus install.
run: |
cd $LibrariesPath/opus
sudo make install
cd $LibrariesPath
sudo cp -R opus-cache/. /
- name: Libiconv cache.
id: cache-libiconv
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/${{ env.LIBICONV_VER }}
path: ${{ env.LibrariesPath }}/libiconv-cache
key: ${{ runner.OS }}-${{ env.LIBICONV_VER }}-${{ env.CACHE_KEY }}
- name: Libiconv.
if: steps.cache-libiconv.outputs.cache-hit != 'true'
@@ -201,10 +205,11 @@ jobs:
cd $LIBICONV_VER
CFLAGS="$MIN_MAC $UNGUARDED" CPPFLAGS="$MIN_MAC $UNGUARDED" LDFLAGS="$MIN_MAC" ./configure --enable-static --prefix=$PREFIX
make -j$(nproc)
sudo make DESTDIR="$LibrariesPath/libiconv-cache" install
- name: Libiconv install.
run: |
cd $LibrariesPath/$LIBICONV_VER
sudo make install
cd $LibrariesPath
sudo cp -R libiconv-cache/. /
- name: FFmpeg cache.
id: cache-ffmpeg
@@ -419,8 +424,8 @@ jobs:
run: |
cd $LibrariesPath
git clone git://code.qt.io/qt/qt5.git qt$QT
cd qt$QT
git clone git://code.qt.io/qt/qt5.git qt_$QT
cd qt_$QT
perl init-repository --module-subset=qtbase,qtimageformats
git checkout v5.12.8
git submodule update qtbase
@@ -449,6 +454,36 @@ jobs:
make clean
cp -r $QT_PREFIX $LibrariesPath/qt-cache
- name: WebRTC cache.
id: cache-webrtc
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/tg_owt
key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}
- name: WebRTC.
if: steps.cache-webrtc.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
git clone $GIT/desktop-app/tg_owt.git
mkdir -p tg_owt/out/Debug
cd tg_owt/out/Debug
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug \
-DTG_OWT_SPECIAL_TARGET=mac \
-DTG_OWT_LIBJPEG_INCLUDE_PATH=`pwd`/../../../qt_$QT/qtbase/src/3rdparty/libjpeg \
-DTG_OWT_OPENSSL_INCLUDE_PATH=`pwd`/../../../openssl_$OPENSSL_VER/include \
-DTG_OWT_OPUS_INCLUDE_PATH=$PREFIX/include/opus \
-DTG_OWT_FFMPEG_INCLUDE_PATH=/usr/local/include \
../..
ninja
# Cleanup.
cd $LibrariesPath/tg_owt
mv out/Debug/libtg_owt.a libtg_owt.a
rm -rf out
mkdir -p out/Debug
mv libtg_owt.a out/Debug/libtg_owt.a
- name: Telegram Desktop build.
if: env.ONLY_CACHE == 'false'
run: |

View File

@@ -41,8 +41,8 @@ on:
jobs:
linux:
name: Ubuntu 20.04
runs-on: ubuntu-20.04
name: Ubuntu
runs-on: ubuntu-latest
env:
UPLOAD_ARTIFACT: "false"
@@ -55,18 +55,22 @@ jobs:
- name: First set up.
run: |
# Workaround for permanent problems with third-party repository keys
sudo rm -rf /etc/apt/sources.list.d/*
sudo apt-get purge --autoremove lxd
sudo apt-get update
sudo snap install --classic snapcraft
sudo snap install lxd
# Workaround for snapcraft
# See https://forum.snapcraft.io/t/13258
sudo chown root:root /
sudo usermod -aG lxd $USER
sudo snap run lxd init --auto
sudo snap run lxd waitready
- name: Telegram Desktop snap build.
run: sudo snap run snapcraft --destructive-mode
run: sg lxd -c 'snap run snapcraft --use-lxd'
- name: Move artifact.
if: env.UPLOAD_ARTIFACT == 'true'

View File

@@ -60,6 +60,7 @@ jobs:
VC: "call vcvars32.bat && cd Libraries"
GIT: "https://github.com"
QT: "5_12_8"
QT_VER: "5.12.8"
OPENSSL_VER: "1_1_1"
UPLOAD_ARTIFACT: "false"
ONLY_CACHE: "false"
@@ -121,6 +122,20 @@ jobs:
cd Patches
eval $checkoutCommit
- name: Find any version of Python 2.
shell: cmd
run: |
echo Find any version of Python 2.
for /D %%a in (C:\hostedtoolcache\windows\Python\2.*) do (
SET PY2=%%a\x64
)
if [%PY2%] == [] (
echo Python 2 is not found.
exit 1
)
echo Found %PY2%.
echo ::set-env name=PY2::%PY2%
- name: LZMA.
shell: cmd
run: |
@@ -154,7 +169,7 @@ jobs:
move ossl_static.pdb out32.dbg\ossl_static
nmake clean
move out32.dbg\ossl_static out32.dbg\ossl_static.pdb
perl Configure no-shared VC-WIN32
perl Configure no-shared no-tests VC-WIN32
nmake
mkdir out32
move libcrypto.lib out32
@@ -191,11 +206,12 @@ jobs:
cd openal-soft
git checkout fix_capture
cd build
cmake ^
cmake .. ^
-G "Visual Studio 16 2019" ^
-A Win32 ^
-D LIBTYPE:STRING=STATIC ^
-D FORCE_STATIC_VCRT:STRING=ON ..
-D FORCE_STATIC_VCRT=ON ^
-D ALSOFT_BACKEND_WASAPI=OFF
msbuild -m OpenAL.vcxproj /property:Configuration=Debug
@@ -214,16 +230,6 @@ jobs:
run: |
cd %LibrariesPath%
echo Find any version of Python 2.
for /D %%a in (C:\hostedtoolcache\windows\Python\2.*) do (
SET PY2=%%a\x64
)
IF [%PY2%] == [] (
echo Python 2 is not found.
exit 1
)
echo Found %PY2%.
git clone %GIT%/telegramdesktop/gyp.git
cd gyp
SET PATH=%PY2%;%cd%;%PATH%
@@ -293,7 +299,7 @@ jobs:
id: cache-qt
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/Qt-5.12.8
path: ${{ env.LibrariesPath }}/Qt-${{ env.QT_VER }}
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_5_12_8/*') }}
- name: Configure Qt 5.12.8.
if: steps.cache-qt.outputs.cache-hit != 'true'
@@ -304,18 +310,18 @@ jobs:
git clone git://code.qt.io/qt/qt5.git qt_%QT%
cd qt_%QT%
perl init-repository --module-subset=qtbase,qtimageformats
git checkout v5.12.8
git checkout v%QT_VER%
git submodule update qtbase
git submodule update qtimageformats
cd qtbase
for /r %%i in (..\..\patches\qtbase_%QT%\*) do git apply %%i
cd ..
SET SSL=%LibrariesPath%\openssl_1_1_1
SET SSL=%LibrariesPath%\openssl_%OPENSSL_VER%
SET LIBS=libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib
configure ^
-prefix "%LibrariesPath%\Qt-5.12.8" ^
-prefix "%LibrariesPath%\Qt-%QT_VER%" ^
-debug ^
-force-debug-info ^
-opensource ^
@@ -343,6 +349,57 @@ jobs:
cd ..
rmdir /S /Q qt_%QT%
- name: WebRTC cache.
id: cache-webrtc
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/tg_owt
key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}
- name: WebRTC.
if: steps.cache-webrtc.outputs.cache-hit != 'true'
shell: cmd
run: |
%VC%
:: Qt libjpeg.
mkdir qt_%QT%
cd qt_%QT%
git clone -b %QT_VER% https://github.com/qt/qtbase
move qtbase\src\3rdparty\libjpeg ..
cd ..
dir
rmdir /S /Q qt_%QT%
mkdir qt_%QT%\qtbase\src\3rdparty\
move libjpeg qt_%QT%\qtbase\src\3rdparty\
:: WebRTC.
cd %LibrariesPath%
git clone %GIT%/desktop-app/tg_owt.git
mkdir tg_owt\out\Debug
cd tg_owt\out\Debug
cmake -G Ninja ^
-DCMAKE_BUILD_TYPE=Debug ^
-DTG_OWT_SPECIAL_TARGET=win ^
-DTG_OWT_LIBJPEG_INCLUDE_PATH=%cd%/../../../qt_%QT%/qtbase/src/3rdparty/libjpeg ^
-DTG_OWT_OPENSSL_INCLUDE_PATH=%cd%/../../../openssl_%OPENSSL_VER%/include ^
-DTG_OWT_OPUS_INCLUDE_PATH=%cd%/../../../opus/include ^
-DTG_OWT_FFMPEG_INCLUDE_PATH=%cd%/../../../ffmpeg ^
../..
ninja
:: Cleanup.
cd %LibrariesPath%\tg_owt
move out\Debug\tg_owt.lib tg_owt.lib
rmdir /S /Q out
mkdir out\Debug
move tg_owt.lib out\Debug\tg_owt.lib
cd %LibrariesPath%
rmdir /S /Q qt_%QT%
- name: Read defines.
shell: bash
run: |

3
.gitmodules vendored
View File

@@ -1,9 +1,6 @@
[submodule "Telegram/ThirdParty/libtgvoip"]
path = Telegram/ThirdParty/libtgvoip
url = https://github.com/telegramdesktop/libtgvoip
[submodule "Telegram/ThirdParty/variant"]
path = Telegram/ThirdParty/variant
url = https://github.com/desktop-app/variant.git
[submodule "Telegram/ThirdParty/GSL"]
path = Telegram/ThirdParty/GSL
url = https://github.com/Microsoft/GSL.git

View File

@@ -36,5 +36,9 @@ include(cmake/options.cmake)
include(cmake/external/qt/package.cmake)
set(desktop_app_skip_libs
variant
)
add_subdirectory(cmake)
add_subdirectory(Telegram)

View File

@@ -34,6 +34,7 @@ Version **1.8.15** was the last that supports older systems
* Qt 5.12.8, 5.6.2 and 5.3.2 slightly patched ([LGPL](http://doc.qt.io/qt-5/lgpl.html))
* OpenSSL 1.1.1 and 1.0.1 ([OpenSSL License](https://www.openssl.org/source/license.html))
* WebRTC ([New BSD License](https://github.com/desktop-app/tg_owt/blob/master/src/LICENSE))
* zlib 1.2.11 ([zlib License](http://www.zlib.net/zlib_license.html))
* LZMA SDK 9.20 ([public domain](http://www.7-zip.org/sdk.html))
* liblzma ([public domain](http://tukaani.org/xz/))
@@ -45,7 +46,6 @@ Version **1.8.15** was the last that supports older systems
* Opus codec ([BSD License](http://www.opus-codec.org/license/))
* FFmpeg ([LGPL](https://www.ffmpeg.org/legal.html))
* Guideline Support Library ([MIT License](https://github.com/Microsoft/GSL/blob/master/LICENSE))
* Mapbox Variant ([BSD License](https://github.com/mapbox/variant/blob/master/LICENSE))
* Range-v3 ([Boost License](https://github.com/ericniebler/range-v3/blob/master/LICENSE.txt))
* Open Sans font ([Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html))
* Vazir font ([License](https://github.com/rastikerdar/vazir-font/blob/master/LICENSE))

View File

@@ -21,21 +21,21 @@ add_subdirectory(lib_qr)
add_subdirectory(lib_webrtc)
add_subdirectory(codegen)
include(CheckCXXSourceCompiles)
include(lib_ui/cmake/generate_styles.cmake)
include(cmake/generate_lang.cmake)
include(cmake/generate_numbers.cmake)
get_filename_component(src_loc SourceFiles REALPATH)
get_filename_component(res_loc Resources REALPATH)
include(cmake/telegram_options.cmake)
include(cmake/lib_export.cmake)
include(cmake/lib_ffmpeg.cmake)
include(cmake/lib_mtproto.cmake)
include(cmake/lib_scheme.cmake)
include(cmake/lib_tgvoip.cmake)
include(cmake/lib_tgcalls.cmake)
include(cmake/td_export.cmake)
include(cmake/td_mtproto.cmake)
include(cmake/td_lang.cmake)
include(cmake/td_scheme.cmake)
include(cmake/td_ui.cmake)
set(style_files
boxes/boxes.style
@@ -61,14 +61,45 @@ set(dependent_style_files
${submodules_loc}/lib_ui/ui/basic.style
${submodules_loc}/lib_ui/ui/layers/layers.style
${submodules_loc}/lib_ui/ui/widgets/widgets.style
${src_loc}/ui/td_common.style
)
generate_styles(Telegram ${src_loc} "${style_files}" "${dependent_style_files}")
generate_lang(Telegram ${res_loc}/langs/lang.strings)
generate_numbers(Telegram ${res_loc}/numbers.txt)
set_target_properties(Telegram PROPERTIES AUTOMOC ON AUTORCC ON)
target_link_libraries(Telegram
PRIVATE
tdesktop::lib_tgcalls_legacy
tdesktop::lib_tgcalls
tdesktop::lib_tgvoip
tdesktop::td_export
tdesktop::td_mtproto
tdesktop::td_lang
tdesktop::td_scheme
tdesktop::td_ui
desktop-app::lib_webrtc
desktop-app::lib_base
desktop-app::lib_crl
desktop-app::lib_ui
desktop-app::lib_tl
desktop-app::lib_storage
desktop-app::lib_lottie
desktop-app::lib_qr
desktop-app::lib_ffmpeg
desktop-app::external_lz4
desktop-app::external_rlottie
desktop-app::external_zlib
desktop-app::external_minizip
desktop-app::external_qt
desktop-app::external_qr_code_generator
desktop-app::external_crash_reports
desktop-app::external_auto_updates
desktop-app::external_openssl
desktop-app::external_openal
)
if (LINUX)
target_link_libraries(Telegram
PRIVATE
@@ -95,52 +126,17 @@ if (LINUX)
desktop-app::external_hime_qt
)
endif()
endif()
if (add_hunspell_library)
target_link_libraries(Telegram PRIVATE desktop-app::external_hunspell)
endif()
if (DESKTOP_APP_USE_PACKAGED AND Qt5WaylandClient_VERSION VERSION_LESS 5.13.0)
find_package(PkgConfig REQUIRED)
pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client)
target_link_libraries(Telegram
PRIVATE
tdesktop::lib_tgcalls_legacy
tdesktop::lib_tgcalls
tdesktop::lib_tgvoip
tdesktop::lib_mtproto
tdesktop::lib_scheme
tdesktop::lib_export
desktop-app::lib_webrtc
desktop-app::lib_base
desktop-app::lib_crl
desktop-app::lib_ui
desktop-app::lib_tl
desktop-app::lib_storage
desktop-app::lib_lottie
desktop-app::lib_qr
desktop-app::lib_ffmpeg
desktop-app::external_lz4
desktop-app::external_rlottie
desktop-app::external_zlib
desktop-app::external_minizip
desktop-app::external_qt
desktop-app::external_qr_code_generator
desktop-app::external_crash_reports
desktop-app::external_auto_updates
desktop-app::external_openssl
desktop-app::external_openal
)
target_include_directories(Telegram
PRIVATE
${WAYLAND_CLIENT_INCLUDE_DIRS}
)
endif()
if (LINUX AND DESKTOP_APP_USE_PACKAGED AND Qt5WaylandClient_VERSION VERSION_LESS 5.13.0)
find_package(PkgConfig REQUIRED)
pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client)
target_include_directories(Telegram
PRIVATE
${WAYLAND_CLIENT_INCLUDE_DIRS}
)
endif()
if (LINUX)
if (DESKTOP_APP_USE_PACKAGED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(XCB_SCREENSAVER REQUIRED IMPORTED_TARGET xcb-screensaver)
@@ -155,34 +151,38 @@ if (LINUX)
target_link_static_libraries(Telegram PRIVATE xcb-screensaver)
target_link_libraries(Telegram PRIVATE xcb)
endif()
endif()
if (LINUX AND NOT TDESKTOP_DISABLE_GTK_INTEGRATION)
find_package(PkgConfig REQUIRED)
target_compile_options(Telegram PRIVATE -Wno-register)
pkg_check_modules(GLIB2 REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GOBJECT REQUIRED IMPORTED_TARGET gobject-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
if (DESKTOP_APP_USE_PACKAGED AND NOT DESKTOP_APP_USE_PACKAGED_LAZY)
pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11)
pkg_check_modules(GOBJECT2 REQUIRED IMPORTED_TARGET gobject-2.0)
pkg_check_modules(GLIB2 REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GTK3 REQUIRED IMPORTED_TARGET gtk+-3.0)
target_link_libraries(Telegram
PRIVATE
PkgConfig::GLIB2
PkgConfig::GOBJECT
PkgConfig::GIO
)
target_link_libraries(Telegram
PRIVATE
PkgConfig::X11
PkgConfig::GOBJECT2
PkgConfig::GLIB2
PkgConfig::GTK3
)
else()
pkg_search_module(GTK REQUIRED gtk+-2.0 gtk+-3.0)
target_link_libraries(Telegram
PRIVATE
X11
gobject-2.0
glib-2.0
)
target_include_directories(Telegram PRIVATE ${GTK_INCLUDE_DIRS})
target_compile_definitions(Telegram PRIVATE G_LOG_DOMAIN="Telegram")
if (NOT TDESKTOP_DISABLE_GTK_INTEGRATION)
find_package(PkgConfig REQUIRED)
if (DESKTOP_APP_USE_PACKAGED AND NOT DESKTOP_APP_USE_PACKAGED_LAZY)
pkg_check_modules(GTK3 REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11)
target_link_libraries(Telegram
PRIVATE
PkgConfig::GTK3
PkgConfig::X11
)
else()
pkg_search_module(GTK REQUIRED gtk+-2.0 gtk+-3.0)
target_include_directories(Telegram PRIVATE ${GTK_INCLUDE_DIRS})
target_link_libraries(Telegram PRIVATE X11)
endif()
endif()
endif()
@@ -191,6 +191,8 @@ nice_target_sources(Telegram ${src_loc}
PRIVATE
${style_files}
api/api_authorizations.cpp
api/api_authorizations.h
api/api_bot.cpp
api/api_bot.h
api/api_chat_filters.cpp
@@ -218,6 +220,8 @@ PRIVATE
api/api_single_message_search.h
api/api_text_entities.cpp
api/api_text_entities.h
api/api_toggling_media.cpp
api/api_toggling_media.h
api/api_updates.cpp
api/api_updates.h
boxes/filters/edit_filter_box.cpp
@@ -467,6 +471,8 @@ PRIVATE
data/data_poll.h
data/data_pts_waiter.cpp
data/data_pts_waiter.h
data/data_replies_list.cpp
data/data_replies_list.h
data/data_reply_preview.cpp
data/data_reply_preview.h
data/data_search_controller.cpp
@@ -588,10 +594,14 @@ PRIVATE
history/view/history_view_message.cpp
history/view/history_view_message.h
history/view/history_view_object.h
history/view/history_view_replies_section.cpp
history/view/history_view_replies_section.h
history/view/history_view_schedule_box.cpp
history/view/history_view_schedule_box.h
history/view/history_view_scheduled_section.cpp
history/view/history_view_scheduled_section.h
history/view/history_view_send_action.cpp
history/view/history_view_send_action.h
history/view/history_view_service_message.cpp
history/view/history_view_service_message.h
history/view/history_view_top_bar_widget.cpp
@@ -711,23 +721,12 @@ PRIVATE
intro/intro_widget.h
lang/lang_cloud_manager.cpp
lang/lang_cloud_manager.h
lang/lang_file_parser.cpp
lang/lang_file_parser.h
lang/lang_hardcoded.h
lang/lang_instance.cpp
lang/lang_instance.h
lang/lang_keys.cpp
lang/lang_keys.h
lang/lang_numbers_animation.cpp
lang/lang_numbers_animation.h
lang/lang_tag.cpp
lang/lang_tag.h
lang/lang_text_entity.cpp
lang/lang_text_entity.h
lang/lang_translator.cpp
lang/lang_translator.h
lang/lang_values.cpp
lang/lang_values.h
main/main_account.cpp
main/main_account.h
main/main_app_config.cpp
@@ -1141,6 +1140,7 @@ PRIVATE
qt_static_plugins.cpp
settings.cpp
settings.h
stdafx.h
)
if (NOT LINUX)
@@ -1230,18 +1230,6 @@ elseif (APPLE)
)
endif()
endif()
elseif (LINUX)
if (NOT TDESKTOP_DISABLE_GTK_INTEGRATION)
find_package(PkgConfig REQUIRED)
pkg_search_module(GTK REQUIRED gtk+-2.0 gtk+-3.0)
target_include_directories(Telegram PRIVATE ${GTK_INCLUDE_DIRS})
if (DESKTOP_APP_USE_PACKAGED)
find_library(X11_LIBRARY X11)
target_link_libraries(Telegram PRIVATE ${X11_LIBRARY})
endif()
endif()
endif()
if (build_macstore)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 767 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 B

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 661 B

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 696 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -370,6 +370,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_adaptive_wide" = "Adaptive layout for wide screens";
"lng_settings_section_call_settings" = "Calls Settings";
"lng_settings_call_camera" = "Camera";
"lng_settings_call_section_output" = "Speakers and headphones";
"lng_settings_call_section_input" = "Microphone";
"lng_settings_call_input_device" = "Input device";
@@ -380,7 +381,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_call_stop_mic_test" = "Stop test";
"lng_settings_call_section_other" = "Other settings";
"lng_settings_call_open_system_prefs" = "Open system sound preferences";
"lng_settings_call_device_default" = "Default";
"lng_settings_call_device_default" = "Same as the System";
"lng_settings_call_audio_ducking" = "Mute other sounds during calls";
"lng_settings_language" = "Language";
@@ -811,6 +812,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_set_group_photo" = "Set Photo";
"lng_profile_add_participant" = "Add Members";
"lng_profile_view_channel" = "View Channel";
"lng_profile_view_discussion" = "View discussion";
"lng_profile_join_channel" = "Join Channel";
"lng_profile_join_group" = "Join Group";
"lng_profile_delete_and_exit" = "Leave";
@@ -1219,6 +1221,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_recent_stickers" = "Frequently used";
"lng_faved_stickers_add" = "Add to Favorites";
"lng_faved_stickers_remove" = "Remove from Favorites";
"lng_recent_stickers_remove" = "Remove from Recent";
"lng_group_stickers" = "Group stickers";
"lng_group_stickers_description" = "You can choose a sticker set which will be available for every member while in the group chat.";
"lng_group_stickers_add" = "Choose sticker set";
@@ -1317,6 +1320,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_message_ph" = "Write a message...";
"lng_broadcast_ph" = "Broadcast a message...";
"lng_broadcast_silent_ph" = "Silent broadcast...";
"lng_send_anonymous_ph" = "Send anonymously...";
"lng_record_cancel" = "Release outside this field to cancel";
"lng_will_be_notified" = "Members will be notified when you post";
"lng_wont_be_notified" = "Members will not be notified when you post";
@@ -1328,7 +1332,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_restart_button" = "Restart";
"lng_channel_mute" = "Mute";
"lng_channel_unmute" = "Unmute";
"lng_channel_discuss" = "Discuss";
"lng_saved_messages" = "Saved Messages";
"lng_saved_short" = "Save";
"lng_saved_forward_here" = "Forward messages here for quick access";
@@ -1342,6 +1345,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_scheduled_send_now_many#one" = "Send {count} message now?";
"lng_scheduled_send_now_many#other" = "Send {count} messages now?";
"lng_replies_view#one" = "View {count} Reply";
"lng_replies_view#other" = "View {count} Replies";
"lng_replies_view_thread" = "View Thread";
"lng_replies_header#one" = "{count} reply";
"lng_replies_header#other" = "{count} replies";
"lng_replies_header_none" = "Replies";
"lng_comments_header#one" = "{count} comment";
"lng_comments_header#other" = "{count} comments";
"lng_comments_header_none" = "Comments";
"lng_comments_open_count#one" = "{count} comment";
"lng_comments_open_count#other" = "{count} comments";
"lng_comments_open_none" = "Leave a comment";
"lng_replies_view_original" = "View in chat";
"lng_replies_messages" = "Replies";
"lng_replies_discussion_started" = "Discussion started";
"lng_replies_no_comments" = "No comments here yet...";
"lng_archived_name" = "Archived chats";
"lng_archived_add" = "Archive";
"lng_archived_remove" = "Unarchive";
@@ -1437,6 +1457,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_unpin_from_top" = "Unpin from top";
"lng_context_mark_unread" = "Mark as unread";
"lng_context_mark_read" = "Mark as read";
"lng_context_mark_read_sure" = "Are you sure you want to mark all chats from this folder as read?";
"lng_context_mark_read_all" = "Mark all chats as read";
"lng_context_mark_read_all_sure" = "Are you sure you want to mark all chats as read?";
"lng_context_archive_expand" = "Expand";
"lng_context_archive_collapse" = "Collapse";
"lng_context_archive_to_menu" = "Move to main menu";
@@ -1724,6 +1747,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_call_error_not_available" = "Sorry, {user} doesn't accept calls.";
"lng_call_error_outdated" = "{user}'s app does not support calls. They need to update their app before you can call them.";
"lng_call_error_no_camera" = "No camera could be found. Please make sure that your camera is connected to the computer.";
"lng_call_error_camera_not_started" = "You can switch to video call once you're connected.";
"lng_call_error_camera_outdated" = "{user}'s app does not support video calls. They need to update their app.";
"lng_call_error_audio_io" = "There seems to be a problem with audio playback on your computer. Please make sure that your computer's speakers and microphone are working and try again.";
"lng_call_bar_hangup" = "End call";
@@ -1816,6 +1842,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_group_invite" = "Add users";
"lng_rights_group_pin" = "Pin messages";
"lng_rights_group_delete" = "Delete messages";
"lng_rights_group_anonymous" = "Remain Anonymous";
"lng_rights_add_admins" = "Add new admins";
"lng_rights_chat_read" = "Read messages";
"lng_rights_chat_send_text" = "Send messages";
@@ -1848,6 +1875,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_transfer_done_group" = "{user} is now the owner of the group.";
"lng_rights_transfer_done_channel" = "{user} is now the owner of the channel.";
"lng_bots_password_confirm_check_about" = "You can finish this action only if you have:";
"lng_bots_password_confirm_title" = "Two-step verification";
"lng_bots_password_confirm_description" = "Please enter your password to confirm the action.";
"lng_restricted_send_message" = "The admins of this group restricted you from writing here.";
"lng_restricted_send_media" = "The admins of this group restricted you from posting media content here.";
"lng_restricted_send_stickers" = "The admins of this group restricted you from posting stickers here.";

View File

@@ -206,7 +206,7 @@
53;CU;Cuba;53 XXXX XXXX;10;
52;MX;Mexico;
51;PE;Peru;51 XXX XXX XXX;11;
49;DE;Germany;49 XXX XXXXXXXX;13;
49;DE;Germany;
48;PL;Poland;48 XXX XXX XXX;11;
47;NO;Norway;47 XXXX XXXX;10;
46;SE;Sweden;46 XX XXX XXXX;11;

View File

@@ -128,7 +128,7 @@ channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.
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 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#f0e6672a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true 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 stats_dc:flags.12?int pts:int = ChatFull;
channelFull#f0e6672a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int = ChatFull;
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
@@ -141,8 +141,8 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#d20b9f3c flags:# has_video:flags.0?true photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto;
messageEmpty#83e5de54 id:int = 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;
message#58ae39c9 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?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
messageService#286fa604 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?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
@@ -192,6 +192,7 @@ photoSizeEmpty#e17e23c type:string = PhotoSize;
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize;
photoSizeProgressive#5aa86a51 type:string location:FileLocation w:int h:int sizes:Vector<int> = PhotoSize;
geoPointEmpty#1117dd5f = GeoPoint;
geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint;
@@ -231,8 +232,6 @@ contact#f911c994 user_id:int mutual:Bool = Contact;
importedContact#d0028438 user_id:int client_id:long = ImportedContact;
contactBlocked#561bc879 user_id:int date:int = ContactBlocked;
contactStatus#d3680c61 user_id:int status:UserStatus = ContactStatus;
contacts.contactsNotModified#b74ba9d2 = contacts.Contacts;
@@ -240,8 +239,8 @@ contacts.contacts#eae87e42 contacts:Vector<Contact> saved_count:int users:Vector
contacts.importedContacts#77d01c3b imported:Vector<ImportedContact> popular_invites:Vector<PopularContact> retry_contacts:Vector<long> users:Vector<User> = contacts.ImportedContacts;
contacts.blocked#1c138d15 blocked:Vector<ContactBlocked> users:Vector<User> = contacts.Blocked;
contacts.blockedSlice#900802a1 count:int blocked:Vector<ContactBlocked> users:Vector<User> = contacts.Blocked;
contacts.blocked#ade1591 blocked:Vector<PeerBlocked> chats:Vector<Chat> users:Vector<User> = contacts.Blocked;
contacts.blockedSlice#e1664194 count:int blocked:Vector<PeerBlocked> chats:Vector<Chat> users:Vector<User> = contacts.Blocked;
messages.dialogs#15ba6c40 dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
messages.dialogsSlice#71e094f3 count:int dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
@@ -292,7 +291,6 @@ updateEncryptedMessagesRead#38fe25b7 chat_id:int max_date:int date:int = Update;
updateChatParticipantAdd#ea4b0e5c chat_id:int user_id:int inviter_id:int date:int version:int = Update;
updateChatParticipantDelete#6e5f8c22 chat_id:int user_id:int version:int = Update;
updateDcOptions#8e5e9873 dc_options:Vector<DcOption> = Update;
updateUserBlocked#80ece81a user_id:int blocked:Bool = Update;
updateNotifySettings#bec268ef peer:NotifyPeer notify_settings:PeerNotifySettings = Update;
updateServiceNotification#ebe46819 flags:# popup:flags.0?true inbox_date:flags.1?int type:string message:string media:MessageMedia entities:Vector<MessageEntity> = Update;
updatePrivacy#ee3b272a key:PrivacyKey rules:Vector<PrivacyRule> = Update;
@@ -358,6 +356,11 @@ updateDialogFilterOrder#a5d72105 order:Vector<int> = Update;
updateDialogFilters#3504914f = Update;
updatePhoneCallSignalingData#2661bf09 phone_call_id:long data:bytes = Update;
updateChannelParticipant#65d2b464 flags:# channel_id:int date:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant qts:int = Update;
updateChannelMessageForwards#6e8a84df channel_id:int id:int forwards:int = Update;
updateReadChannelDiscussionInbox#1cc7de54 flags:# channel_id:int top_msg_id:int read_max_id:int broadcast_id:flags.0?int broadcast_post:flags.0?int = Update;
updateReadChannelDiscussionOutbox#4638a26c channel_id:int top_msg_id:int read_max_id:int = Update;
updatePeerBlocked#246a4b22 peer_id:Peer blocked:Bool = Update;
updateChannelUserTyping#ff2abe9f flags:# channel_id:int top_msg_id:flags.0?int user_id:int action:SendMessageAction = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@@ -367,8 +370,8 @@ updates.differenceSlice#a8fb1981 new_messages:Vector<Message> new_encrypted_mess
updates.differenceTooLong#4afe8f6d pts:int = updates.Difference;
updatesTooLong#e317af7e = Updates;
updateShortMessage#914fbf11 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int entities:flags.7?Vector<MessageEntity> = Updates;
updateShortChatMessage#16812688 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int entities:flags.7?Vector<MessageEntity> = Updates;
updateShortMessage#2296d2c8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector<MessageEntity> = Updates;
updateShortChatMessage#402d5dbb flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector<MessageEntity> = Updates;
updateShort#78d4dec1 update:Update date:int = Updates;
updatesCombined#725b04c3 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq_start:int seq:int = Updates;
updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates;
@@ -547,7 +550,7 @@ botInfo#98e81d3a user_id:int description:string commands:Vector<BotCommand> = Bo
keyboardButton#a2fa4880 text:string = KeyboardButton;
keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton;
keyboardButtonCallback#683a5e46 text:string data:bytes = KeyboardButton;
keyboardButtonCallback#35bbdb6b flags:# requires_password:flags.0?true text:string data:bytes = KeyboardButton;
keyboardButtonRequestPhone#b16a6c29 text:string = KeyboardButton;
keyboardButtonRequestGeoLocation#fc796b3f text:string = KeyboardButton;
keyboardButtonSwitchInline#568a748 flags:# same_peer:flags.0?true text:string query:string = KeyboardButton;
@@ -601,7 +604,7 @@ channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:
channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant;
channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant;
channelParticipantCreator#808d15a4 flags:# user_id:int rank:flags.0?string = ChannelParticipant;
channelParticipantCreator#447dca4b flags:# user_id:int admin_rights:ChatAdminRights rank:flags.0?string = ChannelParticipant;
channelParticipantAdmin#ccbebbaf flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant;
channelParticipantBanned#1c0facaf flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChatBannedRights = ChannelParticipant;
@@ -648,7 +651,7 @@ messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_off
exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink;
messageFwdHeader#353a686b flags:# from_id:flags.0?int from_name:flags.5?string date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int psa_type:flags.6?string = MessageFwdHeader;
messageFwdHeader#5f777dce flags:# from_id:flags.0?Peer from_name:flags.5?string date:int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int psa_type:flags.6?string = MessageFwdHeader;
auth.codeTypeSms#72a3158c = auth.CodeType;
auth.codeTypeCall#741cd3e3 = auth.CodeType;
@@ -1029,7 +1032,7 @@ chatOnlines#f041e250 onlines:int = ChatOnlines;
statsURL#47a971e0 url:string = StatsURL;
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true = ChatAdminRights;
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true = ChatAdminRights;
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights;
@@ -1150,6 +1153,27 @@ stats.megagroupStats#ef7ff916 period:StatsDateRangeDays members:StatsAbsValueAnd
globalPrivacySettings#bea2f424 flags:# archive_and_mute_new_noncontact_peers:flags.0?Bool = GlobalPrivacySettings;
help.countryCode#4203c5ef flags:# country_code:string prefixes:flags.0?Vector<string> patterns:flags.1?Vector<string> = help.CountryCode;
help.country#c3878e23 flags:# hidden:flags.0?true iso2:string default_name:string name:flags.1?string country_codes:Vector<help.CountryCode> = help.Country;
help.countriesListNotModified#93cc1f32 = help.CountriesList;
help.countriesList#87d0759e countries:Vector<help.Country> hash:int = help.CountriesList;
messageViews#455b853d flags:# views:flags.0?int forwards:flags.1?int replies:flags.2?MessageReplies = MessageViews;
messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> users:Vector<User> = messages.MessageViews;
stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats;
messages.discussionMessage#f5dd8f9d flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
messageReplyHeader#a6d57763 flags:# reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
messageReplies#4128faac flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?int max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;
peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -1257,8 +1281,8 @@ contacts.getContacts#c023849f hash:int = contacts.Contacts;
contacts.importContacts#2c800be5 contacts:Vector<InputContact> = contacts.ImportedContacts;
contacts.deleteContacts#96a0e00 id:Vector<InputUser> = Updates;
contacts.deleteByPhones#1013fd9e phones:Vector<string> = Bool;
contacts.block#332b49fc id:InputUser = Bool;
contacts.unblock#e54100bd id:InputUser = Bool;
contacts.block#68cc1411 id:InputPeer = Bool;
contacts.unblock#bea65d50 id:InputPeer = Bool;
contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked;
contacts.search#11f812d8 q:string limit:int = contacts.Found;
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
@@ -1270,19 +1294,20 @@ contacts.toggleTopPeers#8514bdda enabled:Bool = Bool;
contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string = Updates;
contacts.acceptContact#f831a20f id:InputUser = Updates;
contacts.getLocated#d348bc44 flags:# background:flags.1?true geo_point:InputGeoPoint self_expires:flags.0?int = Updates;
contacts.blockFromReplies#29a8962c flags:# delete_message:flags.0?true delete_history:flags.1?true report_spam:flags.2?true msg_id:int = Updates;
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
messages.getDialogs#a0ee3b73 flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs;
messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
messages.search#8614ef68 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
messages.search#4e17810b flags:# peer:InputPeer q:string from_id:flags.0?InputUser top_msg_id:flags.1?int filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages;
messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int = messages.AffectedHistory;
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.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates;
messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true 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;
@@ -1299,8 +1324,8 @@ messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerp
messages.discardEncryption#edd923c5 chat_id:int = Bool;
messages.setEncryptedTyping#791451ed peer:InputEncryptedChat typing:Bool = Bool;
messages.readEncryptedHistory#7f4b690a peer:InputEncryptedChat max_date:int = Bool;
messages.sendEncrypted#a9776773 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
messages.sendEncryptedFile#9a901b66 peer:InputEncryptedChat random_id:long data:bytes file:InputEncryptedFile = messages.SentEncryptedMessage;
messages.sendEncrypted#44fa7a15 flags:# silent:flags.0?true peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
messages.sendEncryptedFile#5559481d flags:# silent:flags.0?true peer:InputEncryptedChat random_id:long data:bytes file:InputEncryptedFile = messages.SentEncryptedMessage;
messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
messages.receivedQueue#55a5bb66 max_qts:int = Vector<long>;
messages.reportEncryptedSpam#4b0c8c0f peer:InputEncryptedChat = Bool;
@@ -1315,10 +1340,10 @@ messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet
messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult;
messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;
messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates;
messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector<int> increment:Bool = Vector<int>;
messages.getMessagesViews#5784d3e1 peer:InputPeer id:Vector<int> increment:Bool = messages.MessageViews;
messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool;
messages.migrateChat#15a3b8e3 chat_id:int = Updates;
messages.searchGlobal#bf7225a4 flags:# folder_id:flags.0?int q:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
messages.searchGlobal#4bc6589a flags:# folder_id:flags.0?int q:string filter:MessagesFilter min_date:int max_date:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
messages.reorderStickerSets#78337739 flags:# masks:flags.0?true order:Vector<long> = Bool;
messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document;
messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
@@ -1329,7 +1354,7 @@ messages.sendInlineBotResult#220815b0 flags:# silent:flags.5?true background:fla
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
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.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = 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;
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
@@ -1394,6 +1419,9 @@ messages.getSuggestedDialogFilters#a29cd42c = Vector<DialogFilterSuggested>;
messages.updateDialogFilter#1ad4a04a flags:# id:int filter:flags.0?DialogFilter = Bool;
messages.updateDialogFiltersOrder#c563c1e4 order:Vector<int> = Bool;
messages.getOldFeaturedStickers#5fe7025b offset:int limit:int hash:int = messages.FeaturedStickers;
messages.getReplies#24b581ba peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage;
messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@@ -1434,6 +1462,7 @@ help.editUserInfo#66b91b70 user_id:InputUser message:string entities:Vector<Mess
help.getPromoData#c0977421 = help.PromoData;
help.hidePromoData#1e251c95 peer:InputPeer = Bool;
help.dismissSuggestion#77fa99f suggestion:string = Bool;
help.getCountriesList#735787a8 lang_code:string hash:int = help.CountriesList;
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
@@ -1454,7 +1483,7 @@ channels.joinChannel#24b524c5 channel:InputChannel = Updates;
channels.leaveChannel#f836aa95 channel:InputChannel = Updates;
channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> = Updates;
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.exportMessageLink#ceb77163 channel:InputChannel id:int grouped:Bool = ExportedMessageLink;
channels.exportMessageLink#e63fadeb flags:# grouped:flags.0?true thread:flags.1?true channel:InputChannel id:int = ExportedMessageLink;
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.getAdminedPublicChannels#f8b036af flags:# by_location:flags.0?true check_limit:flags.1?true = messages.Chats;
channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_rights:ChatBannedRights = Updates;
@@ -1511,5 +1540,7 @@ folders.deleteFolder#1c295881 folder_id:int = Updates;
stats.getBroadcastStats#ab42441a flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastStats;
stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel = stats.MegagroupStats;
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
// LAYER 117
// LAYER 119

View File

@@ -9,7 +9,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="2.3.0.0" />
Version="2.4.1.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName>

View File

@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,3,0,0
PRODUCTVERSION 2,3,0,0
FILEVERSION 2,4,1,0
PRODUCTVERSION 2,4,1,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "2.3.0.0"
VALUE "FileVersion", "2.4.1.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.3.0.0"
VALUE "ProductVersion", "2.4.1.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,3,0,0
PRODUCTVERSION 2,3,0,0
FILEVERSION 2,4,1,0
PRODUCTVERSION 2,4,1,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "2.3.0.0"
VALUE "FileVersion", "2.4.1.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.3.0.0"
VALUE "ProductVersion", "2.4.1.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -0,0 +1,166 @@
/*
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 "api/api_authorizations.h"
#include "apiwrap.h"
#include "base/unixtime.h"
#include "core/changelogs.h"
#include "lang/lang_keys.h"
namespace Api {
namespace {
constexpr auto TestApiId = 17349;
constexpr auto DesktopApiId = 2040;
Authorizations::Entry ParseEntry(const MTPDauthorization &data) {
auto result = Authorizations::Entry();
result.hash = data.is_current() ? 0 : data.vhash().v;
result.incomplete = data.is_password_pending();
const auto apiId = data.vapi_id().v;
const auto isTest = (apiId == TestApiId);
const auto isDesktop = (apiId == DesktopApiId) || isTest;
const auto appName = isDesktop
? QString("Telegram Desktop%1").arg(isTest ? " (GitHub)" : QString())
: qs(data.vapp_name());// +qsl(" for ") + qs(d.vplatform());
const auto appVer = [&] {
const auto version = qs(data.vapp_version());
if (isDesktop) {
const auto verInt = version.toInt();
if (version == QString::number(verInt)) {
return Core::FormatVersionDisplay(verInt);
}
} else {
if (const auto index = version.indexOf('('); index >= 0) {
return version.mid(index);
}
}
return version;
}();
result.name = QString("%1%2")
.arg(appName)
.arg(appVer.isEmpty() ? QString() : (' ' + appVer));
const auto country = qs(data.vcountry());
const auto platform = qs(data.vplatform());
//const auto &countries = countriesByISO2();
//const auto j = countries.constFind(country);
//if (j != countries.cend()) {
// country = QString::fromUtf8(j.value()->name);
//}
result.activeTime = data.vdate_active().v
? data.vdate_active().v
: data.vdate_created().v;
result.info = QString("%1, %2%3")
.arg(qs(data.vdevice_model()))
.arg(platform.isEmpty() ? QString() : platform + ' ')
.arg(qs(data.vsystem_version()));
result.ip = qs(data.vip())
+ (country.isEmpty()
? QString()
: QString::fromUtf8(" \xe2\x80\x93 ") + country);
if (!result.hash) {
result.active = tr::lng_status_online(tr::now);
} else {
const auto now = QDateTime::currentDateTime();
const auto lastTime = base::unixtime::parse(result.activeTime);
const auto nowDate = now.date();
const auto lastDate = lastTime.date();
if (lastDate == nowDate) {
result.active = lastTime.toString(cTimeFormat());
} else if (lastDate.year() == nowDate.year()
&& lastDate.weekNumber() == nowDate.weekNumber()) {
result.active = langDayOfWeek(lastDate);
} else {
result.active = lastDate.toString(qsl("d.MM.yy"));
}
}
return result;
}
} // namespace
Authorizations::Authorizations(not_null<ApiWrap*> api)
: _api(&api->instance()) {
}
void Authorizations::reload() {
if (_requestId) {
return;
}
_requestId = _api.request(MTPaccount_GetAuthorizations(
)).done([=](const MTPaccount_Authorizations &result) {
_requestId = 0;
_lastReceived = crl::now();
result.match([&](const MTPDaccount_authorizations &auths) {
_list = (
auths.vauthorizations().v
) | ranges::view::transform([](const MTPAuthorization &d) {
return ParseEntry(d.c_authorization());
}) | ranges::to<List>;
_listChanges.fire({});
});
}).fail([=](const RPCError &error) {
_requestId = 0;
}).send();
}
void Authorizations::cancelCurrentRequest() {
_api.request(base::take(_requestId)).cancel();
}
void Authorizations::requestTerminate(
Fn<void(const MTPBool &result)> &&done,
Fn<void(const RPCError &error)> &&fail,
std::optional<uint64> hash) {
auto request = hash
? MTPaccount_ResetAuthorization(MTP_long(*hash))
: MTPaccount_ResetAuthorization();
_api.request(std::move(request))
.done(std::move(done))
.fail(std::move(fail))
.send();
}
Authorizations::List Authorizations::list() const {
return _list;
}
auto Authorizations::listChanges() const
-> rpl::producer<Authorizations::List> {
return rpl::single(
list()
) | rpl::then(
_listChanges.events() | rpl::map([=] { return list(); }));
}
rpl::producer<int> Authorizations::totalChanges() const {
return rpl::single(
total()
) | rpl::then(
_listChanges.events() | rpl::map([=] { return total(); }));
}
int Authorizations::total() const {
return ranges::count_if(
_list,
ranges::not_fn(&Entry::incomplete));
}
crl::time Authorizations::lastReceivedTime() {
return _lastReceived;
}
} // namespace Api

View File

@@ -0,0 +1,54 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "mtproto/sender.h"
class ApiWrap;
namespace Api {
class Authorizations final {
public:
explicit Authorizations(not_null<ApiWrap*> api);
struct Entry {
uint64 hash = 0;
bool incomplete = false;
TimeId activeTime = 0;
QString name, active, info, ip;
};
using List = std::vector<Entry>;
void reload();
void cancelCurrentRequest();
void requestTerminate(
Fn<void(const MTPBool &result)> &&done,
Fn<void(const RPCError &error)> &&fail,
std::optional<uint64> hash = std::nullopt);
[[nodiscard]] crl::time lastReceivedTime();
[[nodiscard]] List list() const;
[[nodiscard]] rpl::producer<List> listChanges() const;
[[nodiscard]] int total() const;
[[nodiscard]] rpl::producer<int> totalChanges() const;
private:
MTP::Sender _api;
mtpRequestId _requestId = 0;
List _list;
rpl::event_stream<> _listChanges;
crl::time _lastReceived = 0;
};
} // namespace Api

View File

@@ -8,9 +8,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_bot.h"
#include "apiwrap.h"
#include "core/core_cloud_password.h"
#include "api/api_send_progress.h"
#include "boxes/confirm_box.h"
#include "boxes/share_box.h"
#include "boxes/passcode_box.h"
#include "lang/lang_keys.h"
#include "core/click_handler_types.h"
#include "data/data_changes.h"
#include "data/data_peer.h"
@@ -20,10 +23,129 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_components.h"
#include "main/main_session.h"
#include "ui/toast/toast.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
namespace Api {
namespace {
void SendBotCallbackData(
not_null<HistoryItem*> item,
int row,
int column,
std::optional<MTPInputCheckPasswordSRP> password = std::nullopt,
Fn<void(const RPCError &)> handleError = nullptr) {
if (!IsServerMsgId(item->id)) {
return;
}
const auto history = item->history();
const auto session = &history->session();
const auto owner = &history->owner();
const auto api = &session->api();
const auto bot = item->getMessageBot();
const auto fullId = item->fullId();
const auto getButton = [=] {
return HistoryMessageMarkupButton::Get(
owner,
fullId,
row,
column);
};
const auto button = getButton();
if (!button || button->requestId) {
return;
}
using ButtonType = HistoryMessageMarkupButton::Type;
const auto isGame = (button->type == ButtonType::Game);
auto flags = MTPmessages_GetBotCallbackAnswer::Flags(0);
QByteArray sendData;
if (isGame) {
flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_game;
} else if (button->type == ButtonType::Callback
|| button->type == ButtonType::CallbackWithPassword) {
flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_data;
sendData = button->data;
}
const auto withPassword = password.has_value();
if (withPassword) {
flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_password;
}
button->requestId = api->request(MTPmessages_GetBotCallbackAnswer(
MTP_flags(flags),
history->peer->input,
MTP_int(item->id),
MTP_bytes(sendData),
password.value_or(MTP_inputCheckPasswordEmpty())
)).done([=](const MTPmessages_BotCallbackAnswer &result) {
const auto item = owner->message(fullId);
if (!item) {
return;
}
if (const auto button = getButton()) {
button->requestId = 0;
owner->requestItemRepaint(item);
}
result.match([&](const MTPDmessages_botCallbackAnswer &data) {
if (const auto message = data.vmessage()) {
if (data.is_alert()) {
Ui::show(Box<InformBox>(qs(*message)));
} else {
if (withPassword) {
Ui::hideLayer();
}
Ui::Toast::Show(qs(*message));
}
} else if (const auto url = data.vurl()) {
const auto link = qs(*url);
if (!isGame) {
UrlClickHandler::Open(link);
return;
}
const auto scoreLink = AppendShareGameScoreUrl(
session,
link,
item->fullId());
BotGameUrlClickHandler(bot, scoreLink).onClick({});
session->sendProgressManager().update(
history,
Api::SendProgressType::PlayGame);
} else if (withPassword) {
Ui::hideLayer();
}
});
}).fail([=](const RPCError &error) {
const auto item = owner->message(fullId);
if (!item) {
return;
}
// Show error?
if (const auto button = getButton()) {
button->requestId = 0;
owner->requestItemRepaint(item);
}
if (handleError) {
handleError(error);
}
}).send();
session->changes().messageUpdated(
item,
Data::MessageUpdate::Flag::BotCallbackSent
);
}
} // namespace
void SendBotCallbackData(
not_null<HistoryItem*> item,
int row,
int column) {
SendBotCallbackData(item, row, column, MTP_inputCheckPasswordEmpty());
}
void SendBotCallbackDataWithPassword(
not_null<HistoryItem*> item,
int row,
int column) {
@@ -44,74 +166,63 @@ void SendBotCallbackData(
column);
};
const auto button = getButton();
if (!button) {
if (!button || button->requestId) {
return;
}
using ButtonType = HistoryMessageMarkupButton::Type;
const auto isGame = (button->type == ButtonType::Game);
auto flags = MTPmessages_GetBotCallbackAnswer::Flags(0);
QByteArray sendData;
if (isGame) {
flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_game;
} else if (button->type == ButtonType::Callback) {
flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_data;
sendData = button->data;
}
button->requestId = api->request(MTPmessages_GetBotCallbackAnswer(
MTP_flags(flags),
history->peer->input,
MTP_int(item->id),
MTP_bytes(sendData)
)).done([=](const MTPmessages_BotCallbackAnswer &result) {
const auto item = owner->message(fullId);
if (!item) {
return;
}
if (const auto button = getButton()) {
button->requestId = 0;
owner->requestItemRepaint(item);
}
result.match([&](const MTPDmessages_botCallbackAnswer &data) {
if (const auto message = data.vmessage()) {
if (data.is_alert()) {
Ui::show(Box<InformBox>(qs(*message)));
} else {
Ui::Toast::Show(qs(*message));
api->reloadPasswordState();
SendBotCallbackData(item, row, column, MTP_inputCheckPasswordEmpty(), [=](const RPCError &error) {
auto box = PrePasswordErrorBox(
error,
session,
tr::lng_bots_password_confirm_check_about(
tr::now,
Ui::Text::WithEntities));
if (box) {
Ui::show(std::move(box));
} else {
auto lifetime = std::make_shared<rpl::lifetime>();
button->requestId = -1;
api->passwordState(
) | rpl::take(
1
) | rpl::start_with_next([=](const Core::CloudPasswordState &state) mutable {
if (lifetime) {
base::take(lifetime)->destroy();
}
} else if (const auto url = data.vurl()) {
const auto link = qs(*url);
if (!isGame) {
UrlClickHandler::Open(link);
if (const auto button = getButton()) {
if (button->requestId == -1) {
button->requestId = 0;
}
} else {
return;
}
const auto scoreLink = AppendShareGameScoreUrl(
session,
link,
item->fullId());
BotGameUrlClickHandler(bot, scoreLink).onClick({});
session->sendProgressManager().update(
history,
Api::SendProgressType::PlayGame);
}
});
}).fail([=](const RPCError &error) {
const auto item = owner->message(fullId);
if (!item) {
return;
const auto box = std::make_shared<QPointer<PasscodeBox>>();
auto fields = PasscodeBox::CloudFields::From(state);
fields.customTitle = tr::lng_bots_password_confirm_title();
fields.customDescription
= tr::lng_bots_password_confirm_description(tr::now);
fields.customSubmitButton = tr::lng_passcode_submit();
fields.customCheckCallback = [=](
const Core::CloudPasswordResult &result) {
if (const auto button = getButton()) {
if (button->requestId) {
return;
}
} else {
return;
}
if (const auto item = owner->message(fullId)) {
SendBotCallbackData(item, row, column, result.result, [=](const RPCError &error) {
if (*box) {
(*box)->handleCustomCheckError(error);
}
});
}
};
*box = Ui::show(Box<PasscodeBox>(session, fields));
}, *lifetime);
}
// Show error?
if (const auto button = getButton()) {
button->requestId = 0;
owner->requestItemRepaint(item);
}
}).send();
session->changes().messageUpdated(
item,
Data::MessageUpdate::Flag::BotCallbackSent
);
});
}
} // namespace Api

View File

@@ -16,4 +16,9 @@ void SendBotCallbackData(
int row,
int column);
void SendBotCallbackDataWithPassword(
not_null<HistoryItem*> item,
int row,
int column);
} // namespace Api

View File

@@ -16,6 +16,7 @@ namespace Api {
namespace {
constexpr auto kCancelTypingActionTimeout = crl::time(5000);
constexpr auto kSetMyActionForMs = 10 * crl::time(1000);
} // namespace
@@ -27,7 +28,14 @@ SendProgressManager::SendProgressManager(not_null<Main::Session*> session)
void SendProgressManager::cancel(
not_null<History*> history,
SendProgressType type) {
const auto i = _requests.find({ history, type });
cancel(history, 0, type);
}
void SendProgressManager::cancel(
not_null<History*> history,
MsgId topMsgId,
SendProgressType type) {
const auto i = _requests.find(Key{ history, topMsgId, type });
if (i != _requests.end()) {
_session->api().request(i->second).cancel();
_requests.erase(i);
@@ -42,29 +50,58 @@ void SendProgressManager::cancelTyping(not_null<History*> history) {
void SendProgressManager::update(
not_null<History*> history,
SendProgressType type,
int32 progress) {
int progress) {
update(history, 0, type, progress);
}
void SendProgressManager::update(
not_null<History*> history,
MsgId topMsgId,
SendProgressType type,
int progress) {
const auto peer = history->peer;
if (peer->isSelf() || (peer->isChannel() && !peer->isMegagroup())) {
return;
}
const auto doing = (progress >= 0);
if (history->mySendActionUpdated(type, doing)) {
cancel(history, type);
const auto key = Key{ history, topMsgId, type };
if (updated(key, doing)) {
cancel(history, topMsgId, type);
if (doing) {
send(history, type, progress);
send(key, progress);
}
}
}
void SendProgressManager::send(
not_null<History*> history,
SendProgressType type,
int32 progress) {
bool SendProgressManager::updated(const Key &key, bool doing) {
const auto now = crl::now();
const auto i = _updated.find(key);
if (doing) {
if (i == end(_updated)) {
_updated.emplace(key, now + kSetMyActionForMs);
} else if (i->second > now + (kSetMyActionForMs / 2)) {
return false;
} else {
i->second = now + kSetMyActionForMs;
}
} else {
if (i == end(_updated)) {
return false;
} else if (i->second <= now) {
return false;
} else {
_updated.erase(i);
}
}
return true;
}
void SendProgressManager::send(const Key &key, int progress) {
using Type = SendProgressType;
const auto action = [&]() -> MTPsendMessageAction {
const auto p = MTP_int(progress);
switch (type) {
switch (key.type) {
case Type::Typing: return MTP_sendMessageTypingAction();
case Type::RecordVideo: return MTP_sendMessageRecordVideoAction();
case Type::UploadVideo: return MTP_sendMessageUploadVideoAction(p);
@@ -81,15 +118,19 @@ void SendProgressManager::send(
}
}();
const auto requestId = _session->api().request(MTPmessages_SetTyping(
history->peer->input,
MTP_flags(key.topMsgId
? MTPmessages_SetTyping::Flag::f_top_msg_id
: MTPmessages_SetTyping::Flag(0)),
key.history->peer->input,
MTP_int(key.topMsgId),
action
)).done([=](const MTPBool &result, mtpRequestId requestId) {
done(result, requestId);
}).send();
_requests.emplace(Key{ history, type }, requestId);
_requests.emplace(key, requestId);
if (type == Type::Typing) {
_stopTypingHistory = history;
if (key.type == Type::Typing) {
_stopTypingHistory = key.history;
_stopTypingTimer.callOnce(kCancelTypingActionTimeout);
}
}

View File

@@ -55,7 +55,16 @@ public:
void update(
not_null<History*> history,
SendProgressType type,
int32 progress = 0);
int progress = 0);
void update(
not_null<History*> history,
MsgId topMsgId,
SendProgressType type,
int progress = 0);
void cancel(
not_null<History*> history,
MsgId topMsgId,
SendProgressType type);
void cancel(
not_null<History*> history,
SendProgressType type);
@@ -64,22 +73,26 @@ public:
private:
struct Key {
not_null<History*> history;
MsgId topMsgId = 0;
SendProgressType type = SendProgressType();
inline bool operator<(const Key &other) const {
return (history < other.history)
|| (history == other.history && type < other.type);
|| (history == other.history && topMsgId < other.topMsgId)
|| (history == other.history
&& topMsgId == other.topMsgId
&& type < other.type);
}
};
void send(
not_null<History*> history,
SendProgressType type,
int32 progress);
bool updated(const Key &key, bool doing);
void send(const Key &key, int progress);
void done(const MTPBool &result, mtpRequestId requestId);
const not_null<Main::Session*> _session;
base::flat_map<Key, mtpRequestId> _requests;
base::flat_map<Key, crl::time> _updated;
base::Timer _stopTypingTimer;
History *_stopTypingHistory = nullptr;

View File

@@ -39,10 +39,12 @@ void InnerFillMessagePostFlags(
const Api::SendOptions &options,
not_null<PeerData*> peer,
MTPDmessage::Flags &flags) {
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
if (!channelPost) {
const auto anonymousPost = peer->amAnonymous();
if (!anonymousPost) {
flags |= MTPDmessage::Flag::f_from_id;
return;
} else if (peer->asMegagroup()) {
return;
}
flags |= MTPDmessage::Flag::f_post;
// Don't display views and author of a new post when it's scheduled.
@@ -79,18 +81,18 @@ void SendExistingMedia(
auto clientFlags = NewMessageClientFlags();
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (message.action.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
flags |= MTPDmessage::Flag::f_reply_to;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
}
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto anonymousPost = peer->amAnonymous();
const auto silentPost = message.action.options.silent
|| (channelPost && session->data().notifySilentPosts(peer));
|| (peer->isBroadcast() && session->data().notifySilentPosts(peer));
InnerFillMessagePostFlags(message.action.options, peer, flags);
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
auto messageFromId = channelPost ? 0 : session->userId();
auto messagePostAuthor = channelPost ? session->user()->name : QString();
auto messageFromId = anonymousPost ? 0 : session->userPeerId();
auto messagePostAuthor = peer->isBroadcast() ? session->user()->name : QString();
auto caption = TextWithEntities{
message.textWithTags.text,
@@ -249,18 +251,19 @@ bool SendDice(Api::MessageToSend &message) {
auto clientFlags = NewMessageClientFlags();
auto sendFlags = MTPmessages_SendMedia::Flags(0);
if (message.action.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
flags |= MTPDmessage::Flag::f_reply_to;
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
}
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto replyHeader = NewMessageReplyHeader(message.action);
const auto anonymousPost = peer->amAnonymous();
const auto silentPost = message.action.options.silent
|| (channelPost && session->data().notifySilentPosts(peer));
|| (peer->isBroadcast() && session->data().notifySilentPosts(peer));
InnerFillMessagePostFlags(message.action.options, peer, flags);
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}
auto messageFromId = channelPost ? 0 : session->userId();
auto messagePostAuthor = channelPost ? session->user()->name : QString();
auto messageFromId = anonymousPost ? 0 : session->userPeerId();
auto messagePostAuthor = peer->isBroadcast() ? session->user()->name : QString();
const auto replyTo = message.action.replyTo;
if (message.action.options.scheduled) {
@@ -272,23 +275,27 @@ bool SendDice(Api::MessageToSend &message) {
session->data().registerMessageRandomId(randomId, newId);
const auto views = 1;
const auto forwards = 0;
history->addNewMessage(
MTP_message(
MTP_flags(flags),
MTP_int(newId.msg),
MTP_int(messageFromId),
peerToMTP(messageFromId),
peerToMTP(history->peer->id),
MTPMessageFwdHeader(),
MTP_int(0),
MTP_int(replyTo),
MTPint(), // via_bot_id
replyHeader,
MTP_int(HistoryItem::NewMessageDate(
message.action.options.scheduled)),
MTP_string(),
MTP_messageMediaDice(MTP_int(0), MTP_string(emoji)),
MTPReplyMarkup(),
MTP_vector<MTPMessageEntity>(),
MTP_int(1),
MTPint(),
MTP_int(views),
MTP_int(forwards),
MTPMessageReplies(),
MTPint(), // edit_date
MTP_string(messagePostAuthor),
MTPlong(),
//MTPMessageReactions(),
@@ -387,9 +394,10 @@ void SendConfirmedFile(
| MTPDmessage::Flag::f_media;
auto clientFlags = NewMessageClientFlags();
if (file->to.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
flags |= MTPDmessage::Flag::f_reply_to;
}
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto replyHeader = NewMessageReplyHeader(action);
const auto anonymousPost = peer->amAnonymous();
const auto silentPost = file->to.options.silent;
Api::FillMessagePostFlags(action, peer, flags);
if (silentPost) {
@@ -406,11 +414,13 @@ void SendConfirmedFile(
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
const auto messageFromId = channelPost ? 0 : session->userId();
const auto messagePostAuthor = channelPost
const auto messageFromId = anonymousPost ? 0 : session->userPeerId();
const auto messagePostAuthor = peer->isBroadcast()
? session->user()->name
: QString();
const auto views = 1;
const auto forwards = 0;
if (file->type == SendMediaType::Photo) {
const auto photoFlags = MTPDmessageMediaPhoto::Flag::f_photo | 0;
const auto photo = MTP_messageMediaPhoto(
@@ -421,18 +431,20 @@ void SendConfirmedFile(
const auto mtpMessage = MTP_message(
MTP_flags(flags),
MTP_int(newId.msg),
MTP_int(messageFromId),
peerToMTP(messageFromId),
peerToMTP(file->to.peer),
MTPMessageFwdHeader(),
MTPint(),
MTP_int(file->to.replyTo),
replyHeader,
MTP_int(HistoryItem::NewMessageDate(file->to.options.scheduled)),
MTP_string(caption.text),
photo,
MTPReplyMarkup(),
localEntities,
MTP_int(1),
MTPint(),
MTP_int(views),
MTP_int(forwards),
MTPMessageReplies(),
MTPint(), // edit_date
MTP_string(messagePostAuthor),
MTP_long(groupId),
//MTPMessageReactions(),
@@ -457,18 +469,20 @@ void SendConfirmedFile(
const auto mtpMessage = MTP_message(
MTP_flags(flags),
MTP_int(newId.msg),
MTP_int(messageFromId),
peerToMTP(messageFromId),
peerToMTP(file->to.peer),
MTPMessageFwdHeader(),
MTPint(),
MTP_int(file->to.replyTo),
replyHeader,
MTP_int(HistoryItem::NewMessageDate(file->to.options.scheduled)),
MTP_string(caption.text),
document,
MTPReplyMarkup(),
localEntities,
MTP_int(1),
MTPint(),
MTP_int(views),
MTP_int(forwards),
MTPMessageReplies(),
MTPint(), // edit_date
MTP_string(messagePostAuthor),
MTP_long(groupId),
//MTPMessageReactions(),
@@ -496,19 +510,21 @@ void SendConfirmedFile(
MTP_message(
MTP_flags(flags),
MTP_int(newId.msg),
MTP_int(messageFromId),
peerToMTP(messageFromId),
peerToMTP(file->to.peer),
MTPMessageFwdHeader(),
MTPint(),
MTP_int(file->to.replyTo),
replyHeader,
MTP_int(
HistoryItem::NewMessageDate(file->to.options.scheduled)),
MTP_string(caption.text),
document,
MTPReplyMarkup(),
localEntities,
MTP_int(1),
MTPint(),
MTP_int(views),
MTP_int(forwards),
MTPMessageReplies(),
MTPint(), // edit_date
MTP_string(messagePostAuthor),
MTP_long(groupId),
//MTPMessageReactions(),

View File

@@ -0,0 +1,117 @@
/*
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 "api/api_toggling_media.h"
#include "apiwrap.h"
#include "data/data_document.h"
#include "data/data_file_origin.h"
#include "data/data_session.h"
#include "data/stickers/data_stickers.h"
#include "main/main_session.h"
namespace Api {
namespace {
template <typename ToggleRequest, typename DoneCallback>
void ToggleExistingMedia(
not_null<DocumentData*> document,
Data::FileOrigin origin,
ToggleRequest toggleRequest,
DoneCallback &&done) {
const auto api = &document->owner().session().api();
auto performRequest = [=](const auto &repeatRequest) -> void {
const auto usedFileReference = document->fileReference();
api->request(std::move(
toggleRequest
)).done([=](const MTPBool &result) {
if (mtpIsTrue(result)) {
done();
}
}).fail([=](const RPCError &error) {
if (error.code() == 400
&& error.type().startsWith(u"FILE_REFERENCE_"_q)) {
auto refreshed = [=](const Data::UpdatedFileReferences &d) {
if (document->fileReference() != usedFileReference) {
repeatRequest(repeatRequest);
}
};
api->refreshFileReference(origin, std::move(refreshed));
}
}).send();
};
performRequest(performRequest);
}
} // namespace
void ToggleFavedSticker(
not_null<DocumentData*> document,
Data::FileOrigin origin) {
ToggleFavedSticker(
document,
std::move(origin),
!document->owner().stickers().isFaved(document));
}
void ToggleFavedSticker(
not_null<DocumentData*> document,
Data::FileOrigin origin,
bool faved) {
if (faved && !document->sticker()) {
return;
}
ToggleExistingMedia(
document,
std::move(origin),
MTPmessages_FaveSticker(document->mtpInput(), MTP_bool(!faved)),
[=] { document->owner().stickers().setFaved(document, faved); });
}
void ToggleRecentSticker(
not_null<DocumentData*> document,
Data::FileOrigin origin,
bool saved) {
if (!document->sticker()) {
return;
}
auto done = [=] {
if (!saved) {
document->owner().stickers().removeFromRecentSet(document);
}
};
ToggleExistingMedia(
document,
std::move(origin),
MTPmessages_SaveRecentSticker(
MTP_flags(MTPmessages_SaveRecentSticker::Flag(0)),
document->mtpInput(),
MTP_bool(!saved)),
std::move(done));
}
void ToggleSavedGif(
not_null<DocumentData*> document,
Data::FileOrigin origin,
bool saved) {
if (saved && !document->isGifv()) {
return;
}
auto done = [=] {
if (saved) {
document->owner().stickers().addSavedGif(document);
}
};
ToggleExistingMedia(
document,
std::move(origin),
MTPmessages_SaveGif(document->mtpInput(), MTP_bool(!saved)),
std::move(done));
}
} // namespace Api

View File

@@ -0,0 +1,31 @@
/*
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 {
void ToggleFavedSticker(
not_null<DocumentData*> document,
Data::FileOrigin origin);
void ToggleFavedSticker(
not_null<DocumentData*> document,
Data::FileOrigin origin,
bool faved);
void ToggleRecentSticker(
not_null<DocumentData*> document,
Data::FileOrigin origin,
bool saved);
void ToggleSavedGif(
not_null<DocumentData*> document,
Data::FileOrigin origin,
bool saved);
} // namespace Api

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_updates.h"
#include "api/api_authorizations.h"
#include "api/api_text_entities.h"
#include "main/main_session.h"
#include "main/main_account.h"
@@ -97,20 +98,9 @@ bool ForwardedInfoDataLoaded(
not_null<Main::Session*> session,
const MTPMessageFwdHeader &header) {
return header.match([&](const MTPDmessageFwdHeader &data) {
if (const auto channelId = data.vchannel_id()) {
if (!session->data().channelLoaded(channelId->v)) {
return false;
}
if (const auto fromId = data.vfrom_id()) {
const auto from = session->data().user(fromId->v);
// Minimal loaded is fine in this case.
if (from->loadedStatus == PeerData::NotLoaded) {
return false;
}
}
} else if (const auto fromId = data.vfrom_id()) {
if (const auto fromId = data.vfrom_id()) {
// Fully loaded is required in this case.
if (!session->data().userLoaded(fromId->v)) {
if (!session->data().peerLoaded(peerFromMTP(*fromId))) {
return false;
}
}
@@ -145,7 +135,7 @@ DataIsLoadedResult AllDataLoadedForMessage(
return message.match([&](const MTPDmessage &message) {
if (const auto fromId = message.vfrom_id()) {
if (!message.is_post()
&& !session->data().userLoaded(fromId->v)) {
&& !session->data().peerLoaded(peerFromMTP(*fromId))) {
return DataIsLoadedResult::FromNotLoaded;
}
}
@@ -168,7 +158,7 @@ DataIsLoadedResult AllDataLoadedForMessage(
}, [&](const MTPDmessageService &message) {
if (const auto fromId = message.vfrom_id()) {
if (!message.is_post()
&& !session->data().userLoaded(fromId->v)) {
&& !session->data().peerLoaded(peerFromMTP(*fromId))) {
return DataIsLoadedResult::FromNotLoaded;
}
}
@@ -890,23 +880,26 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
const auto peerUserId = d.is_out()
? d.vuser_id()
: MTP_int(_session->userId());
const auto fwd = d.vfwd_from();
_session->data().addNewMessage(
MTP_message(
MTP_flags(flags),
d.vid(),
d.is_out() ? MTP_int(_session->userId()) : d.vuser_id(),
MTP_peerUser(peerUserId),
fwd ? (*fwd) : MTPMessageFwdHeader(),
(d.is_out()
? peerToMTP(_session->userPeerId())
: MTP_peerUser(d.vuser_id())),
MTP_peerUser(d.vuser_id()),
d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(),
MTP_int(d.vvia_bot_id().value_or_empty()),
MTP_int(d.vreply_to_msg_id().value_or_empty()),
d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(),
d.vdate(),
d.vmessage(),
MTP_messageMediaEmpty(),
MTPReplyMarkup(),
MTP_vector<MTPMessageEntity>(d.ventities().value_or_empty()),
MTPint(),
MTPint(),
MTPint(), // views
MTPint(), // forwards
MTPMessageReplies(),
MTPint(), // edit_date
MTPstring(),
MTPlong(),
//MTPMessageReactions(),
@@ -917,24 +910,26 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
case mtpc_updateShortChatMessage: {
const auto &d = updates.c_updateShortChatMessage();
const auto flags = mtpCastFlags(d.vflags().v) | MTPDmessage::Flag::f_from_id;
const auto fwd = d.vfwd_from();
const auto flags = mtpCastFlags(d.vflags().v)
| MTPDmessage::Flag::f_from_id;
_session->data().addNewMessage(
MTP_message(
MTP_flags(flags),
d.vid(),
d.vfrom_id(),
MTP_peerUser(d.vfrom_id()),
MTP_peerChat(d.vchat_id()),
fwd ? (*fwd) : MTPMessageFwdHeader(),
d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(),
MTP_int(d.vvia_bot_id().value_or_empty()),
MTP_int(d.vreply_to_msg_id().value_or_empty()),
d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(),
d.vdate(),
d.vmessage(),
MTP_messageMediaEmpty(),
MTPReplyMarkup(),
MTP_vector<MTPMessageEntity>(d.ventities().value_or_empty()),
MTPint(),
MTPint(),
MTPint(), // views
MTPint(), // forwards
MTPMessageReplies(),
MTPint(), // edit_date
MTPstring(),
MTPlong(),
//MTPMessageReactions(),
@@ -1541,26 +1536,51 @@ void Updates::feedUpdate(const MTPUpdate &update) {
const auto user = session().data().userLoaded(d.vuser_id().v);
if (history && user) {
const auto when = requestingDifference() ? 0 : base::unixtime::now();
session().data().registerSendAction(history, user, d.vaction(), when);
session().data().registerSendAction(
history,
MsgId(),
user,
d.vaction(),
when);
}
} break;
case mtpc_updateChatUserTyping: {
auto &d = update.c_updateChatUserTyping();
const auto history = [&]() -> History* {
if (const auto chat = session().data().chatLoaded(d.vchat_id().v)) {
return session().data().historyLoaded(chat->id);
} else if (const auto channel = session().data().channelLoaded(d.vchat_id().v)) {
return session().data().historyLoaded(channel->id);
}
return nullptr;
}();
const auto history = session().data().historyLoaded(
peerFromChat(d.vchat_id()));
const auto user = (d.vuser_id().v == session().userId())
? nullptr
: session().data().userLoaded(d.vuser_id().v);
if (history && user) {
const auto when = requestingDifference() ? 0 : base::unixtime::now();
session().data().registerSendAction(history, user, d.vaction(), when);
session().data().registerSendAction(
history,
MsgId(),
user,
d.vaction(),
when);
}
} break;
case mtpc_updateChannelUserTyping: {
const auto &d = update.c_updateChannelUserTyping();
const auto history = session().data().historyLoaded(
peerFromChannel(d.vchannel_id()));
const auto user = (d.vuser_id().v == session().userId())
? nullptr
: session().data().userLoaded(d.vuser_id().v);
if (history && user) {
const auto when = requestingDifference()
? 0
: base::unixtime::now();
const auto rootId = d.vtop_msg_id().value_or_empty();
session().data().registerSendAction(
history,
rootId,
user,
d.vaction(),
when);
}
} break;
@@ -1731,10 +1751,10 @@ void Updates::feedUpdate(const MTPUpdate &update) {
Core::App().calls().handleUpdate(&session(), update);
} break;
case mtpc_updateUserBlocked: {
const auto &d = update.c_updateUserBlocked();
if (const auto user = session().data().userLoaded(d.vuser_id().v)) {
user->setIsBlocked(mtpIsTrue(d.vblocked()));
case mtpc_updatePeerBlocked: {
const auto &d = update.c_updatePeerBlocked();
if (const auto peer = session().data().peerLoaded(peerFromMTP(d.vpeer_id()))) {
peer->setIsBlocked(mtpIsTrue(d.vblocked()));
}
} break;
@@ -1753,7 +1773,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
}
} else {
session().data().serviceNotification(text, d.vmedia());
session().data().checkNewAuthorization();
session().api().authorizations().reload();
}
} break;
@@ -1912,12 +1932,54 @@ void Updates::feedUpdate(const MTPUpdate &update) {
} break;
case mtpc_updateChannelMessageViews: {
auto &d = update.c_updateChannelMessageViews();
if (auto item = session().data().message(d.vchannel_id().v, d.vid().v)) {
const auto &d = update.c_updateChannelMessageViews();
if (const auto item = session().data().message(d.vchannel_id().v, d.vid().v)) {
item->setViewsCount(d.vviews().v);
}
} break;
case mtpc_updateChannelMessageForwards: {
const auto &d = update.c_updateChannelMessageForwards();
if (const auto item = session().data().message(d.vchannel_id().v, d.vid().v)) {
item->setForwardsCount(d.vforwards().v);
}
} break;
case mtpc_updateReadChannelDiscussionInbox: {
const auto &d = update.c_updateReadChannelDiscussionInbox();
const auto channelId = d.vchannel_id().v;
const auto msgId = d.vtop_msg_id().v;
const auto readTillId = d.vread_max_id().v;
const auto item = session().data().message(channelId, msgId);
if (item) {
item->setRepliesInboxReadTill(readTillId);
if (const auto post = item->lookupDiscussionPostOriginal()) {
post->setRepliesInboxReadTill(readTillId);
}
}
if (const auto broadcastId = d.vbroadcast_id()) {
if (const auto post = session().data().message(
broadcastId->v,
d.vbroadcast_post()->v)) {
post->setRepliesInboxReadTill(readTillId);
}
}
} break;
case mtpc_updateReadChannelDiscussionOutbox: {
const auto &d = update.c_updateReadChannelDiscussionOutbox();
const auto channelId = d.vchannel_id().v;
const auto msgId = d.vtop_msg_id().v;
const auto readTillId = d.vread_max_id().v;
const auto item = session().data().message(channelId, msgId);
if (item) {
item->setRepliesOutboxReadTill(readTillId);
if (const auto post = item->lookupDiscussionPostOriginal()) {
post->setRepliesOutboxReadTill(readTillId);
}
}
} break;
case mtpc_updateChannelAvailableMessages: {
auto &d = update.c_updateChannelAvailableMessages();
if (const auto channel = session().data().channelLoaded(d.vchannel_id().v)) {

View File

@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "apiwrap.h"
#include "api/api_authorizations.h"
#include "api/api_hash.h"
#include "api/api_media.h"
#include "api/api_sending.h"
@@ -158,19 +159,19 @@ std::optional<ApiWrap::Privacy::Key> ApiWrap::Privacy::KeyFromMTP(
return std::nullopt;
}
bool ApiWrap::BlockedUsersSlice::Item::operator==(const Item &other) const {
return (user == other.user) && (date == other.date);
bool ApiWrap::BlockedPeersSlice::Item::operator==(const Item &other) const {
return (peer == other.peer) && (date == other.date);
}
bool ApiWrap::BlockedUsersSlice::Item::operator!=(const Item &other) const {
bool ApiWrap::BlockedPeersSlice::Item::operator!=(const Item &other) const {
return !(*this == other);
}
bool ApiWrap::BlockedUsersSlice::operator==(const BlockedUsersSlice &other) const {
bool ApiWrap::BlockedPeersSlice::operator==(const BlockedPeersSlice &other) const {
return (total == other.total) && (list == other.list);
}
bool ApiWrap::BlockedUsersSlice::operator!=(const BlockedUsersSlice &other) const {
bool ApiWrap::BlockedPeersSlice::operator!=(const BlockedPeersSlice &other) const {
return !(*this == other);
}
@@ -186,6 +187,7 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
//, _feedReadTimer([=] { readFeeds(); }) // #feed
, _topPromotionTimer([=] { refreshTopPromotion(); })
, _updateNotifySettingsTimer([=] { sendNotifySettingsUpdates(); })
, _authorizations(std::make_unique<Api::Authorizations>(this))
, _selfDestruct(std::make_unique<Api::SelfDestruct>(this))
, _sensitiveContent(std::make_unique<Api::SensitiveContent>(this))
, _globalPrivacy(std::make_unique<Api::GlobalPrivacy>(this)) {
@@ -683,17 +685,57 @@ void ApiWrap::finalizeMessageDataRequest(
}
}
QString ApiWrap::exportDirectMessageLink(not_null<HistoryItem*> item) {
QString ApiWrap::exportDirectMessageLink(
not_null<HistoryItem*> item,
bool inRepliesContext) {
Expects(item->history()->peer->isChannel());
const auto itemId = item->fullId();
const auto channel = item->history()->peer->asChannel();
const auto fallback = [&] {
const auto base = channel->hasUsername()
? channel->username
: "c/" + QString::number(channel->bareId());
const auto query = base + '/' + QString::number(item->id);
if (channel->hasUsername() && !channel->isMegagroup()) {
auto linkChannel = channel;
auto linkItemId = item->id;
auto linkCommentId = 0;
auto linkThreadId = 0;
if (inRepliesContext) {
if (const auto rootId = item->replyToTop()) {
const auto root = item->history()->owner().message(
channel->bareId(),
rootId);
const auto sender = root
? root->discussionPostOriginalSender()
: nullptr;
if (sender && sender->hasUsername()) {
// Comment to a public channel.
const auto forwarded = root->Get<HistoryMessageForwarded>();
linkItemId = forwarded->savedFromMsgId;
if (linkItemId) {
linkChannel = sender;
linkCommentId = item->id;
} else {
linkItemId = item->id;
}
} else {
// Reply in a thread, maybe comment in a private channel.
linkThreadId = rootId;
}
}
}
const auto base = linkChannel->hasUsername()
? linkChannel->username
: "c/" + QString::number(linkChannel->bareId());
const auto query = base
+ '/'
+ QString::number(linkItemId)
+ (linkCommentId
? "?comment=" + QString::number(linkCommentId)
: linkThreadId
? "?thread=" + QString::number(linkThreadId)
: "");
if (linkChannel->hasUsername()
&& !linkChannel->isMegagroup()
&& !linkCommentId
&& !linkThreadId) {
if (const auto media = item->media()) {
if (const auto document = media->document()) {
if (document->isVideoMessage()) {
@@ -709,9 +751,11 @@ QString ApiWrap::exportDirectMessageLink(not_null<HistoryItem*> item) {
? i->second
: fallback();
request(MTPchannels_ExportMessageLink(
MTP_flags(inRepliesContext
? MTPchannels_ExportMessageLink::Flag::f_thread
: MTPchannels_ExportMessageLink::Flag(0)),
channel->inputChannel,
MTP_int(item->id),
MTP_bool(false)
MTP_int(item->id)
)).done([=](const MTPExportedMessageLink &result) {
const auto link = result.match([&](const auto &data) {
return qs(data.vlink());
@@ -1495,9 +1539,13 @@ void ApiWrap::applyLastParticipantsList(
});
const auto adminCanEdit = (p.type() == mtpc_channelParticipantAdmin)
? p.c_channelParticipantAdmin().is_can_edit()
: (p.type() == mtpc_channelParticipantCreator)
? channel->amCreator()
: false;
const auto adminRights = (p.type() == mtpc_channelParticipantAdmin)
? p.c_channelParticipantAdmin().vadmin_rights()
: (p.type() == mtpc_channelParticipantCreator)
? p.c_channelParticipantCreator().vadmin_rights()
: emptyAdminRights;
const auto restrictedRights = (p.type() == mtpc_channelParticipantBanned)
? p.c_channelParticipantBanned().vbanned_rights()
@@ -2003,64 +2051,66 @@ void ApiWrap::leaveChannel(not_null<ChannelData*> channel) {
}
}
void ApiWrap::blockUser(not_null<UserData*> user) {
if (user->isBlocked()) {
void ApiWrap::blockPeer(not_null<PeerData*> peer) {
if (peer->isBlocked()) {
session().changes().peerUpdated(
user,
peer,
Data::PeerUpdate::Flag::IsBlocked);
} else if (_blockRequests.find(user) == end(_blockRequests)) {
const auto requestId = request(MTPcontacts_Block(user->inputUser)).done([this, user](const MTPBool &result) {
_blockRequests.erase(user);
user->setIsBlocked(true);
if (_blockedUsersSlice) {
_blockedUsersSlice->list.insert(
_blockedUsersSlice->list.begin(),
{ user, base::unixtime::now() });
++_blockedUsersSlice->total;
_blockedUsersChanges.fire_copy(*_blockedUsersSlice);
} else if (_blockRequests.find(peer) == end(_blockRequests)) {
const auto requestId = request(MTPcontacts_Block(
peer->input
)).done([=](const MTPBool &result) {
_blockRequests.erase(peer);
peer->setIsBlocked(true);
if (_blockedPeersSlice) {
_blockedPeersSlice->list.insert(
_blockedPeersSlice->list.begin(),
{ peer, base::unixtime::now() });
++_blockedPeersSlice->total;
_blockedPeersChanges.fire_copy(*_blockedPeersSlice);
}
}).fail([this, user](const RPCError &error) {
_blockRequests.erase(user);
}).fail([=](const RPCError &error) {
_blockRequests.erase(peer);
}).send();
_blockRequests.emplace(user, requestId);
_blockRequests.emplace(peer, requestId);
}
}
void ApiWrap::unblockUser(not_null<UserData*> user, Fn<void()> onDone) {
if (!user->isBlocked()) {
void ApiWrap::unblockPeer(not_null<PeerData*> peer, Fn<void()> onDone) {
if (!peer->isBlocked()) {
session().changes().peerUpdated(
user,
peer,
Data::PeerUpdate::Flag::IsBlocked);
return;
} else if (_blockRequests.find(user) != end(_blockRequests)) {
} else if (_blockRequests.find(peer) != end(_blockRequests)) {
return;
}
const auto requestId = request(MTPcontacts_Unblock(
user->inputUser
peer->input
)).done([=](const MTPBool &result) {
_blockRequests.erase(user);
user->setIsBlocked(false);
if (_blockedUsersSlice) {
auto &list = _blockedUsersSlice->list;
_blockRequests.erase(peer);
peer->setIsBlocked(false);
if (_blockedPeersSlice) {
auto &list = _blockedPeersSlice->list;
for (auto i = list.begin(); i != list.end(); ++i) {
if (i->user == user) {
if (i->peer == peer) {
list.erase(i);
break;
}
}
if (_blockedUsersSlice->total > list.size()) {
--_blockedUsersSlice->total;
if (_blockedPeersSlice->total > list.size()) {
--_blockedPeersSlice->total;
}
_blockedUsersChanges.fire_copy(*_blockedUsersSlice);
_blockedPeersChanges.fire_copy(*_blockedPeersSlice);
}
if (onDone) {
onDone();
}
}).fail([=](const RPCError &error) {
_blockRequests.erase(user);
_blockRequests.erase(peer);
}).send();
_blockRequests.emplace(user, requestId);
_blockRequests.emplace(peer, requestId);
}
void ApiWrap::exportInviteLink(not_null<PeerData*> peer) {
@@ -2203,7 +2253,7 @@ void ApiWrap::handlePrivacyChange(
void ApiWrap::updatePrivacyLastSeens(const QVector<MTPPrivacyRule> &rules) {
const auto now = base::unixtime::now();
_session->data().enumerateUsers([&](UserData *user) {
if (user->isSelf() || user->loadedStatus != PeerData::FullLoaded) {
if (user->isSelf() || !user->isFullLoaded()) {
return;
}
if (user->onlineTill <= 0) {
@@ -2682,14 +2732,14 @@ void ApiWrap::requestFileReference(
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87122
const auto &origin = p.first;
const auto &reference = p.second;
const auto documentId = base::get_if<DocumentFileLocationId>(
const auto documentId = std::get_if<DocumentFileLocationId>(
&origin);
if (documentId) {
_session->data().document(
documentId->id
)->refreshFileReference(reference);
}
const auto photoId = base::get_if<PhotoFileLocationId>(&origin);
const auto photoId = std::get_if<PhotoFileLocationId>(&origin);
if (photoId) {
_session->data().photo(
photoId->id
@@ -2748,7 +2798,7 @@ void ApiWrap::refreshFileReference(
const auto fail = [&] {
handler(UpdatedFileReferences());
};
origin.data.match([&](Data::FileOriginMessage data) {
v::match(origin.data, [&](Data::FileOriginMessage data) {
if (const auto item = _session->data().message(data)) {
if (item->isScheduled()) {
const auto &scheduled = _session->data().scheduledMessages();
@@ -2824,7 +2874,7 @@ void ApiWrap::refreshFileReference(
MTP_long(data.themeId),
MTP_long(data.accessHash)),
MTP_long(0)));
}, [&](std::nullopt_t) {
}, [&](v::null_t) {
fail();
});
}
@@ -2936,72 +2986,6 @@ std::vector<not_null<DocumentData*>> *ApiWrap::stickersByEmoji(
return nullptr;
}
void ApiWrap::toggleFavedSticker(
not_null<DocumentData*> document,
Data::FileOrigin origin,
bool faved) {
if (faved && !document->sticker()) {
return;
}
auto performRequest = [=](const auto &repeatRequest) -> void {
const auto usedFileReference = document->fileReference();
request(MTPmessages_FaveSticker(
document->mtpInput(),
MTP_bool(!faved)
)).done([=](const MTPBool &result) {
if (mtpIsTrue(result)) {
_session->data().stickers().setFaved(document, faved);
}
}).fail([=](const RPCError &error) {
if (error.code() == 400
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
auto refreshed = [=](const UpdatedFileReferences &data) {
if (document->fileReference() != usedFileReference) {
repeatRequest(repeatRequest);
}
};
refreshFileReference(origin, std::move(refreshed));
}
}).send();
};
performRequest(performRequest);
}
void ApiWrap::toggleSavedGif(
not_null<DocumentData*> document,
Data::FileOrigin origin,
bool saved) {
if (saved && !document->isGifv()) {
return;
}
auto performRequest = [=](const auto &repeatRequest) -> void {
const auto usedFileReference = document->fileReference();
request(MTPmessages_SaveGif(
document->mtpInput(),
MTP_bool(!saved)
)).done([=](const MTPBool &result) {
if (mtpIsTrue(result)) {
if (saved) {
_session->data().stickers().addSavedGif(document);
}
}
}).fail([=](const RPCError &error) {
if (error.code() == 400
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
auto refreshed = [=](const UpdatedFileReferences &data) {
if (document->fileReference() != usedFileReference) {
repeatRequest(repeatRequest);
}
};
refreshFileReference(origin, std::move(refreshed));
}
}).send();
};
performRequest(performRequest);
}
void ApiWrap::requestStickers(TimeId now) {
if (!_session->data().stickers().updateNeeded(now)
|| _stickersUpdateRequest) {
@@ -3962,9 +3946,9 @@ void ApiWrap::forwardMessages(
histories.readInbox(history);
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto anonymousPost = peer->amAnonymous();
const auto silentPost = action.options.silent
|| (channelPost && _session->data().notifySilentPosts(peer));
|| (peer->isBroadcast() && _session->data().notifySilentPosts(peer));
auto flags = MTPDmessage::Flags(0);
auto clientFlags = MTPDmessage_ClientFlags();
@@ -3981,7 +3965,6 @@ void ApiWrap::forwardMessages(
}
auto forwardFrom = items.front()->history()->peer;
auto currentGroupId = items.front()->groupId();
auto ids = QVector<MTPint>();
auto randomIds = QVector<MTPlong>();
auto localIds = std::shared_ptr<base::flat_map<uint64, FullMsgId>>();
@@ -3990,14 +3973,10 @@ void ApiWrap::forwardMessages(
if (shared) {
++shared->requestsLeft;
}
const auto finalFlags = sendFlags
| (currentGroupId == MessageGroupId()
? MTPmessages_ForwardMessages::Flag(0)
: MTPmessages_ForwardMessages::Flag::f_grouped);
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_ForwardMessages(
MTP_flags(finalFlags),
MTP_flags(sendFlags),
forwardFrom->input,
MTP_vector<MTPint>(ids),
MTP_vector<MTPlong>(randomIds),
@@ -4039,10 +4018,10 @@ void ApiWrap::forwardMessages(
peerToChannel(peer->id),
_session->data().nextLocalMessageId());
const auto self = _session->user();
const auto messageFromId = channelPost
? UserId(0)
: peerToUser(self->id);
const auto messagePostAuthor = channelPost
const auto messageFromId = anonymousPost
? PeerId(0)
: self->id;
const auto messagePostAuthor = peer->isBroadcast()
? self->name
: QString();
history->addNewLocalMessage(
@@ -4062,11 +4041,9 @@ void ApiWrap::forwardMessages(
}
const auto newFrom = item->history()->peer;
const auto newGroupId = item->groupId();
if (forwardFrom != newFrom
|| currentGroupId != newGroupId) {
if (forwardFrom != newFrom) {
sendAccumulated();
forwardFrom = newFrom;
currentGroupId = newGroupId;
}
ids.push_back(MTP_int(item->id));
randomIds.push_back(MTP_long(randomId));
@@ -4114,34 +4091,36 @@ void ApiWrap::sendSharedContact(
const auto newId = FullMsgId(
history->channelId(),
_session->data().nextLocalMessageId());
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto anonymousPost = peer->amAnonymous();
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
auto clientFlags = NewMessageClientFlags();
if (action.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
flags |= MTPDmessage::Flag::f_reply_to;
}
const auto replyHeader = NewMessageReplyHeader(action);
FillMessagePostFlags(action, peer, flags);
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
const auto messageFromId = anonymousPost ? 0 : _session->userPeerId();
const auto messagePostAuthor = peer->isBroadcast()
? _session->user()->name
: QString();
const auto vcard = QString();
const auto views = 1;
const auto forwards = 0;
const auto item = history->addNewMessage(
MTP_message(
MTP_flags(flags),
MTP_int(newId.msg),
MTP_int(messageFromId),
peerToMTP(messageFromId),
peerToMTP(peer->id),
MTPMessageFwdHeader(),
MTPint(),
MTP_int(action.replyTo),
MTPint(), // via_bot_id
replyHeader,
MTP_int(HistoryItem::NewMessageDate(action.options.scheduled)),
MTP_string(),
MTP_messageMediaContact(
@@ -4153,7 +4132,9 @@ void ApiWrap::sendSharedContact(
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(),
MTP_int(views),
MTPint(),
MTP_int(forwards),
MTPMessageReplies(),
MTPint(), // edit_date
MTP_string(messagePostAuthor),
MTPlong(),
//MTPMessageReactions(),
@@ -4167,7 +4148,9 @@ void ApiWrap::sendSharedContact(
MTP_string(lastName),
MTP_string(vcard));
auto options = action.options;
options.silent = _session->data().notifySilentPosts(peer);
if (_session->data().notifySilentPosts(peer)) {
options.silent = true;
}
sendMedia(item, media, options);
_session->data().sendHistoryChangeNotifications();
@@ -4375,9 +4358,10 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
auto clientFlags = NewMessageClientFlags();
auto sendFlags = MTPmessages_SendMessage::Flags(0);
if (action.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
flags |= MTPDmessage::Flag::f_reply_to;
sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to_msg_id;
}
const auto replyHeader = NewMessageReplyHeader(action);
MTPMessageMedia media = MTP_messageMediaEmpty();
if (message.webPageId == CancelledWebPageId) {
sendFlags |= MTPmessages_SendMessage::Flag::f_no_webpage;
@@ -4389,9 +4373,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
MTP_int(page->pendingTill)));
flags |= MTPDmessage::Flag::f_media;
}
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto anonymousPost = peer->amAnonymous();
const auto silentPost = action.options.silent
|| (channelPost && _session->data().notifySilentPosts(peer));
|| (peer->isBroadcast() && _session->data().notifySilentPosts(peer));
FillMessagePostFlags(action, peer, flags);
if (silentPost) {
sendFlags |= MTPmessages_SendMessage::Flag::f_silent;
@@ -4411,8 +4395,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
history->clearCloudDraft();
history->setSentDraftText(QString());
}
auto messageFromId = channelPost ? 0 : _session->userId();
auto messagePostAuthor = channelPost
auto messageFromId = anonymousPost ? 0 : _session->userPeerId();
auto messagePostAuthor = peer->isBroadcast()
? _session->user()->name
: QString();
if (action.options.scheduled) {
@@ -4421,23 +4405,27 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
} else {
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
const auto views = 1;
const auto forwards = 0;
lastMessage = history->addNewMessage(
MTP_message(
MTP_flags(flags),
MTP_int(newId.msg),
MTP_int(messageFromId),
peerToMTP(messageFromId),
peerToMTP(peer->id),
MTPMessageFwdHeader(),
MTPint(),
MTP_int(action.replyTo),
MTPint(), // via_bot_id
replyHeader,
MTP_int(
HistoryItem::NewMessageDate(action.options.scheduled)),
msgText,
media,
MTPReplyMarkup(),
localEntities,
MTP_int(1),
MTPint(),
MTP_int(views),
MTP_int(forwards),
MTPMessageReplies(),
MTPint(), // edit_date
MTP_string(messagePostAuthor),
MTPlong(),
//MTPMessageReactions(),
@@ -4524,12 +4512,12 @@ void ApiWrap::sendInlineResult(
auto clientFlags = NewMessageClientFlags();
auto sendFlags = MTPmessages_SendInlineBotResult::Flag::f_clear_draft | 0;
if (action.replyTo) {
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
flags |= MTPDmessage::Flag::f_reply_to;
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_reply_to_msg_id;
}
bool channelPost = peer->isChannel() && !peer->isMegagroup();
bool silentPost = action.options.silent
|| (channelPost && _session->data().notifySilentPosts(peer));
const auto anonymousPost = peer->amAnonymous();
const auto silentPost = action.options.silent
|| (peer->isBroadcast() && _session->data().notifySilentPosts(peer));
FillMessagePostFlags(action, peer, flags);
if (silentPost) {
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_silent;
@@ -4544,8 +4532,8 @@ void ApiWrap::sendInlineResult(
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
}
const auto messageFromId = channelPost ? 0 : _session->userId();
const auto messagePostAuthor = channelPost
const auto messageFromId = anonymousPost ? 0 : _session->userPeerId();
const auto messagePostAuthor = peer->isBroadcast()
? _session->user()->name
: QString();
@@ -5184,34 +5172,34 @@ auto ApiWrap::privacyValue(Privacy::Key key) -> rpl::producer<Privacy> {
}
}
void ApiWrap::reloadBlockedUsers() {
if (_blockedUsersRequestId) {
void ApiWrap::reloadBlockedPeers() {
if (_blockedPeersRequestId) {
return;
}
_blockedUsersRequestId = request(MTPcontacts_GetBlocked(
_blockedPeersRequestId = request(MTPcontacts_GetBlocked(
MTP_int(0),
MTP_int(kBlockedFirstSlice)
)).done([=](const MTPcontacts_Blocked &result) {
_blockedUsersRequestId = 0;
_blockedPeersRequestId = 0;
const auto push = [&](
int count,
const QVector<MTPContactBlocked> &list) {
auto slice = BlockedUsersSlice();
const QVector<MTPPeerBlocked> &list) {
auto slice = BlockedPeersSlice();
slice.total = std::max(count, list.size());
slice.list.reserve(list.size());
for (const auto &contact : list) {
contact.match([&](const MTPDcontactBlocked &data) {
const auto user = _session->data().userLoaded(
data.vuser_id().v);
if (user) {
user->setIsBlocked(true);
slice.list.push_back({ user, data.vdate().v });
contact.match([&](const MTPDpeerBlocked &data) {
const auto peer = _session->data().peerLoaded(
peerFromMTP(data.vpeer_id()));
if (peer) {
peer->setIsBlocked(true);
slice.list.push_back({ peer, data.vdate().v });
}
});
}
if (!_blockedUsersSlice || *_blockedUsersSlice != slice) {
_blockedUsersSlice = slice;
_blockedUsersChanges.fire(std::move(slice));
if (!_blockedPeersSlice || *_blockedPeersSlice != slice) {
_blockedPeersSlice = slice;
_blockedPeersChanges.fire(std::move(slice));
}
};
result.match([&](const MTPDcontacts_blockedSlice &data) {
@@ -5222,17 +5210,21 @@ void ApiWrap::reloadBlockedUsers() {
push(0, data.vblocked().v);
});
}).fail([=](const RPCError &error) {
_blockedUsersRequestId = 0;
_blockedPeersRequestId = 0;
}).send();
}
auto ApiWrap::blockedUsersSlice() -> rpl::producer<BlockedUsersSlice> {
if (!_blockedUsersSlice) {
reloadBlockedUsers();
auto ApiWrap::blockedPeersSlice() -> rpl::producer<BlockedPeersSlice> {
if (!_blockedPeersSlice) {
reloadBlockedPeers();
}
return _blockedUsersSlice
? _blockedUsersChanges.events_starting_with_copy(*_blockedUsersSlice)
: (_blockedUsersChanges.events() | rpl::type_erased());
return _blockedPeersSlice
? _blockedPeersChanges.events_starting_with_copy(*_blockedPeersSlice)
: (_blockedPeersChanges.events() | rpl::type_erased());
}
Api::Authorizations &ApiWrap::authorizations() {
return *_authorizations;
}
Api::SelfDestruct &ApiWrap::selfDestruct() {
@@ -5265,9 +5257,8 @@ void ApiWrap::createPoll(
history->clearLocalDraft();
history->clearCloudDraft();
}
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
const auto silentPost = action.options.silent
|| (channelPost && _session->data().notifySilentPosts(peer));
|| (peer->isBroadcast() && _session->data().notifySilentPosts(peer));
if (silentPost) {
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
}

View File

@@ -52,6 +52,7 @@ struct CloudPasswordState;
namespace Api {
class Updates;
class Authorizations;
class SelfDestruct;
class SensitiveContent;
class GlobalPrivacy;
@@ -121,9 +122,9 @@ public:
static std::optional<Key> KeyFromMTP(mtpTypeId type);
};
struct BlockedUsersSlice {
struct BlockedPeersSlice {
struct Item {
UserData *user = nullptr;
PeerData *peer = nullptr;
TimeId date = 0;
bool operator==(const Item &other) const;
@@ -133,8 +134,8 @@ public:
QVector<Item> list;
int total = 0;
bool operator==(const BlockedUsersSlice &other) const;
bool operator!=(const BlockedUsersSlice &other) const;
bool operator==(const BlockedPeersSlice &other) const;
bool operator!=(const BlockedPeersSlice &other) const;
};
explicit ApiWrap(not_null<Main::Session*> session);
@@ -172,7 +173,9 @@ public:
ChannelData *channel,
MsgId msgId,
RequestMessageDataCallback callback);
QString exportDirectMessageLink(not_null<HistoryItem*> item);
QString exportDirectMessageLink(
not_null<HistoryItem*> item,
bool inRepliesContext);
void requestContacts();
void requestDialogs(Data::Folder *folder = nullptr);
@@ -275,20 +278,12 @@ public:
const MTPInputStickerSet &set);
std::vector<not_null<DocumentData*>> *stickersByEmoji(
not_null<EmojiPtr> emoji);
void toggleFavedSticker(
not_null<DocumentData*> document,
Data::FileOrigin origin,
bool faved);
void toggleSavedGif(
not_null<DocumentData*> document,
Data::FileOrigin origin,
bool saved);
void joinChannel(not_null<ChannelData*> channel);
void leaveChannel(not_null<ChannelData*> channel);
void blockUser(not_null<UserData*> user);
void unblockUser(not_null<UserData*> user, Fn<void()> onDone = nullptr);
void blockPeer(not_null<PeerData*> peer);
void unblockPeer(not_null<PeerData*> peer, Fn<void()> onDone = nullptr);
void exportInviteLink(not_null<PeerData*> peer);
void requestNotifySettings(const MTPInputNotifyPeer &peer);
@@ -456,9 +451,10 @@ public:
void reloadPrivacy(Privacy::Key key);
rpl::producer<Privacy> privacyValue(Privacy::Key key);
void reloadBlockedUsers();
rpl::producer<BlockedUsersSlice> blockedUsersSlice();
void reloadBlockedPeers();
rpl::producer<BlockedPeersSlice> blockedPeersSlice();
[[nodiscard]] Api::Authorizations &authorizations();
[[nodiscard]] Api::SelfDestruct &selfDestruct();
[[nodiscard]] Api::SensitiveContent &sensitiveContent();
[[nodiscard]] Api::GlobalPrivacy &globalPrivacy();
@@ -698,7 +694,7 @@ private:
QMap<uint64, QPair<uint64, mtpRequestId> > _stickerSetRequests;
QMap<ChannelData*, mtpRequestId> _channelAmInRequests;
base::flat_map<not_null<UserData*>, mtpRequestId> _blockRequests;
base::flat_map<not_null<PeerData*>, mtpRequestId> _blockRequests;
base::flat_map<not_null<PeerData*>, mtpRequestId> _exportInviteRequests;
base::flat_map<PeerId, mtpRequestId> _notifySettingRequests;
base::flat_map<not_null<History*>, mtpRequestId> _draftsSaveRequestIds;
@@ -817,10 +813,11 @@ private:
base::flat_map<Privacy::Key, Privacy> _privacyValues;
std::map<Privacy::Key, rpl::event_stream<Privacy>> _privacyChanges;
mtpRequestId _blockedUsersRequestId = 0;
std::optional<BlockedUsersSlice> _blockedUsersSlice;
rpl::event_stream<BlockedUsersSlice> _blockedUsersChanges;
mtpRequestId _blockedPeersRequestId = 0;
std::optional<BlockedPeersSlice> _blockedPeersSlice;
rpl::event_stream<BlockedPeersSlice> _blockedPeersChanges;
const std::unique_ptr<Api::Authorizations> _authorizations;
const std::unique_ptr<Api::SelfDestruct> _selfDestruct;
const std::unique_ptr<Api::SensitiveContent> _sensitiveContent;
const std::unique_ptr<Api::GlobalPrivacy> _globalPrivacy;

View File

@@ -125,7 +125,7 @@ QString telegramFaqLink() {
const auto langpacked = [&](const char *language) {
return result + '/' + language;
};
const auto current = Lang::Current().id();
const auto current = Lang::Id();
for (const auto language : { "de", "es", "it", "ko" }) {
if (current.startsWith(QLatin1String(language))) {
return langpacked(language);

View File

@@ -244,7 +244,6 @@ private:
Fn<void()> _revokeCallback;
mtpRequestId _revokeRequestId = 0;
QPointer<ConfirmBox> _weakRevokeConfirmBox;
};
@@ -1417,21 +1416,21 @@ void RevokePublicLinkBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
lt_group,
pressed->name);
auto confirmText = tr::lng_channels_too_much_public_revoke(tr::now);
_weakRevokeConfirmBox = Ui::show(Box<ConfirmBox>(text, confirmText, crl::guard(this, [this, pressed]() {
auto callback = crl::guard(this, [=](Fn<void()> &&close) {
if (_revokeRequestId) return;
_revokeRequestId = _api.request(MTPchannels_UpdateUsername(
pressed->asChannel()->inputChannel,
MTP_string()
)).done([=](const MTPBool &result) {
const auto callback = _revokeCallback;
if (_weakRevokeConfirmBox) {
_weakRevokeConfirmBox->closeBox();
}
if (callback) {
)).done([=, close = std::move(close)](const MTPBool &result) {
close();
if (const auto callback = _revokeCallback) {
callback();
}
}).send();
})), Ui::LayerOption::KeepOther);
});
Ui::show(
Box<ConfirmBox>(text, confirmText, std::move(callback)),
Ui::LayerOption::KeepOther);
}
}

View File

@@ -103,7 +103,7 @@ private:
return !(*this == other);
}
};
using Selection = base::optional_variant<Selected, DeleteSelected>;
using Selection = std::variant<v::null_t, Selected, DeleteSelected>;
int getSelectionIndex(const Selection &selection) const;
void repaintPaper(int index);
@@ -163,12 +163,9 @@ void BackgroundBox::prepare() {
}
void BackgroundBox::removePaper(const Data::WallPaper &paper) {
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
const auto session = &_controller->session();
const auto remove = [=, weak = Ui::MakeWeak(this)]{
if (*box) {
(*box)->closeBox();
}
const auto remove = [=, weak = Ui::MakeWeak(this)](Fn<void()> &&close) {
close();
if (weak) {
weak->_inner->removePaper(paper);
}
@@ -179,7 +176,7 @@ void BackgroundBox::removePaper(const Data::WallPaper &paper) {
paper.mtpSettings()
)).send();
};
*box = Ui::show(
Ui::show(
Box<ConfirmBox>(
tr::lng_background_sure_delete(tr::now),
tr::lng_selected_delete(tr::now),
@@ -358,16 +355,16 @@ void BackgroundBox::Inner::paintPaper(
p.drawPixmap(x, y, paper.thumbnail);
}
const auto over = _overDown ? _overDown : _over;
const auto over = !v::is_null(_overDown) ? _overDown : _over;
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, checkLeft, checkTop, width());
} else if (Data::IsCloudWallPaper(paper.data)
&& !Data::IsDefaultWallPaper(paper.data)
&& over.has_value()
&& !v::is_null(over)
&& (&paper == &_papers[getSelectionIndex(over)])) {
const auto deleteSelected = over.is<DeleteSelected>();
const auto deleteSelected = v::is<DeleteSelected>(over);
const auto deletePos = QPoint(x + st::backgroundSize.width() - st::stickerPanDeleteIconBg.width(), y);
p.setOpacity(deleteSelected ? st::stickerPanDeleteOpacityBgOver : st::stickerPanDeleteOpacityBg);
st::stickerPanDeleteIconBg.paint(p, deletePos, width());
@@ -414,7 +411,7 @@ void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) {
repaintPaper(getSelectionIndex(_over));
_over = newOver;
repaintPaper(getSelectionIndex(_over));
setCursor((_over.has_value() || _overDown.has_value())
setCursor((!v::is_null(_over) || !v::is_null(_overDown))
? style::cur_pointer
: style::cur_default);
}
@@ -442,22 +439,22 @@ void BackgroundBox::Inner::mousePressEvent(QMouseEvent *e) {
int BackgroundBox::Inner::getSelectionIndex(
const Selection &selection) const {
return selection.match([](const Selected &data) {
return v::match(selection, [](const Selected &data) {
return data.index;
}, [](const DeleteSelected &data) {
return data.index;
}, [](std::nullopt_t) {
}, [](v::null_t) {
return -1;
});
}
void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
if (base::take(_overDown) == _over && _over.has_value()) {
if (base::take(_overDown) == _over && !v::is_null(_over)) {
const auto index = getSelectionIndex(_over);
if (index >= 0 && index < _papers.size()) {
if (base::get_if<DeleteSelected>(&_over)) {
if (std::get_if<DeleteSelected>(&_over)) {
_backgroundRemove.fire_copy(_papers[index].data);
} else if (base::get_if<Selected>(&_over)) {
} else if (std::get_if<Selected>(&_over)) {
auto &paper = _papers[index];
if (!paper.dataMedia) {
if (const auto document = paper.data.document()) {
@@ -468,7 +465,7 @@ void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
_backgroundChosen.fire_copy(paper.data);
}
}
} else if (!_over.has_value()) {
} else if (v::is_null(_over)) {
setCursor(style::cur_default);
}
}

View File

@@ -348,6 +348,7 @@ sessionInfoFont: msgFont;
sessionInfoFg: windowSubTextFg;
sessionTerminateTop: 28px;
sessionTerminateSkip: 22px;
sessionNamePadding: margins(0px, 0px, 5px, 0px);
sessionTerminate: IconButton {
width: 20px;
height: 20px;
@@ -366,6 +367,15 @@ sessionTerminateAllButton: LinkButton(boxLinkButton) {
color: attentionButtonFg;
overColor: attentionButtonFg;
}
sessionNameStyle: TextStyle(defaultTextStyle) {
font: sessionNameFont;
}
sessionWhenStyle: TextStyle(defaultTextStyle) {
font: sessionWhenFont;
}
sessionInfoStyle: TextStyle(defaultTextStyle) {
font: sessionInfoFont;
}
passcodeHeaderFont: font(19px);
passcodeHeaderHeight: 80px;

View File

@@ -74,7 +74,7 @@ TextParseOptions kMarkedTextBoxOptions = {
ConfirmBox::ConfirmBox(
QWidget*,
const QString &text,
FnMut<void()> confirmedCallback,
ConfirmBox::ConfirmedCallback confirmedCallback,
FnMut<void()> cancelledCallback)
: _confirmText(tr::lng_box_ok(tr::now))
, _cancelText(tr::lng_cancel(tr::now))
@@ -89,7 +89,7 @@ ConfirmBox::ConfirmBox(
QWidget*,
const QString &text,
const QString &confirmText,
FnMut<void()> confirmedCallback,
ConfirmBox::ConfirmedCallback confirmedCallback,
FnMut<void()> cancelledCallback)
: _confirmText(confirmText)
, _cancelText(tr::lng_cancel(tr::now))
@@ -104,7 +104,7 @@ ConfirmBox::ConfirmBox(
QWidget*,
const TextWithEntities &text,
const QString &confirmText,
FnMut<void()> confirmedCallback,
ConfirmBox::ConfirmedCallback confirmedCallback,
FnMut<void()> cancelledCallback)
: _confirmText(confirmText)
, _cancelText(tr::lng_cancel(tr::now))
@@ -120,7 +120,7 @@ ConfirmBox::ConfirmBox(
const QString &text,
const QString &confirmText,
const style::RoundButton &confirmStyle,
FnMut<void()> confirmedCallback,
ConfirmBox::ConfirmedCallback confirmedCallback,
FnMut<void()> cancelledCallback)
: _confirmText(confirmText)
, _cancelText(tr::lng_cancel(tr::now))
@@ -136,7 +136,7 @@ ConfirmBox::ConfirmBox(
const QString &text,
const QString &confirmText,
const QString &cancelText,
FnMut<void()> confirmedCallback,
ConfirmBox::ConfirmedCallback confirmedCallback,
FnMut<void()> cancelledCallback)
: _confirmText(confirmText)
, _cancelText(cancelText)
@@ -153,7 +153,7 @@ ConfirmBox::ConfirmBox(
const QString &confirmText,
const style::RoundButton &confirmStyle,
const QString &cancelText,
FnMut<void()> confirmedCallback,
ConfirmBox::ConfirmedCallback confirmedCallback,
FnMut<void()> cancelledCallback)
: _confirmText(confirmText)
, _cancelText(cancelText)
@@ -256,8 +256,16 @@ void ConfirmBox::textUpdated() {
void ConfirmBox::confirmed() {
if (!_confirmed) {
_confirmed = true;
if (auto callback = std::move(_confirmedCallback)) {
callback();
const auto confirmed = &_confirmedCallback;
if (const auto callbackPtr = std::get_if<1>(confirmed)) {
if (auto callback = base::take(*callbackPtr)) {
callback();
}
} else if (const auto callbackPtr = std::get_if<2>(confirmed)) {
if (auto callback = base::take(*callbackPtr)) {
callback([=] { closeBox(); });
}
}
}
}
@@ -808,53 +816,7 @@ void DeleteMessagesBox::deleteAndClear() {
_deleteConfirmedCallback();
}
auto remove = std::vector<not_null<HistoryItem*>>();
remove.reserve(_ids.size());
base::flat_map<not_null<History*>, 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;
}
remove.push_back(item);
if (IsServerMsgId(item->id)) {
idsByPeer[history].push_back(MTP_int(itemId.msg));
}
}
}
for (const auto &[history, ids] : idsByPeer) {
history->owner().histories().deleteMessages(history, 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 &result) {
peer->session().api().applyUpdates(result);
}).send();
}
for (const auto item : remove) {
const auto history = item->history();
const auto wasLast = (history->lastMessage() == item);
const auto wasInChats = (history->chatListMessage() == item);
item->destroy();
if (wasLast || wasInChats) {
history->requestChatListMessage();
}
}
_session->data().histories().deleteMessages(_ids, revoke);
const auto session = _session;
Ui::hideLayer();

View File

@@ -28,12 +28,51 @@ class EmptyUserpic;
class InformBox;
class ConfirmBox : public Ui::BoxContent, public ClickHandlerHost {
public:
ConfirmBox(QWidget*, const QString &text, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const QString &cancelText, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, const QString &cancelText, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
ConfirmBox(QWidget*, const TextWithEntities &text, const QString &confirmText, FnMut<void()> confirmedCallback = nullptr, FnMut<void()> cancelledCallback = nullptr);
using ConfirmedCallback = std::variant<
v::null_t,
FnMut<void()>,
FnMut<void(Fn<void()>)>>;
ConfirmBox(
QWidget*,
const QString &text,
ConfirmedCallback confirmedCallback = FnMut<void()>(),
FnMut<void()> cancelledCallback = FnMut<void()>());
ConfirmBox(
QWidget*,
const QString &text,
const QString &confirmText,
ConfirmedCallback confirmedCallback = FnMut<void()>(),
FnMut<void()> cancelledCallback = FnMut<void()>());
ConfirmBox(
QWidget*,
const QString &text,
const QString &confirmText,
const style::RoundButton &confirmStyle,
ConfirmedCallback confirmedCallback = FnMut<void()>(),
FnMut<void()> cancelledCallback = FnMut<void()>());
ConfirmBox(
QWidget*,
const QString &text,
const QString &confirmText,
const QString &cancelText,
ConfirmedCallback confirmedCallback = FnMut<void()>(),
FnMut<void()> cancelledCallback = FnMut<void()>());
ConfirmBox(
QWidget*,
const QString &text,
const QString &confirmText,
const style::RoundButton &confirmStyle,
const QString &cancelText,
ConfirmedCallback confirmedCallback = FnMut<void()>(),
FnMut<void()> cancelledCallback = FnMut<void()>());
ConfirmBox(
QWidget*,
const TextWithEntities &text,
const QString &confirmText,
ConfirmedCallback confirmedCallback = v::null,
FnMut<void()> cancelledCallback = nullptr);
void updateLink();
@@ -87,7 +126,7 @@ private:
bool _confirmed = false;
bool _cancelled = false;
bool _strictCancel = false;
FnMut<void()> _confirmedCallback;
ConfirmBox::ConfirmedCallback _confirmedCallback;
FnMut<void()> _cancelledCallback;
};

View File

@@ -41,6 +41,48 @@ constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000);
using ProxyData = MTP::ProxyData;
class HostInput : public Ui::MaskedInputField {
public:
HostInput(
QWidget *parent,
const style::InputField &st,
rpl::producer<QString> placeholder,
const QString &val);
protected:
void correctValue(
const QString &was,
int wasCursor,
QString &now,
int &nowCursor) override;
};
HostInput::HostInput(
QWidget *parent,
const style::InputField &st,
rpl::producer<QString> placeholder,
const QString &val)
: MaskedInputField(parent, st, std::move(placeholder), val) {
}
void HostInput::correctValue(
const QString &was,
int wasCursor,
QString &now,
int &nowCursor) {
QString newText;
int newCursor = nowCursor;
newText.reserve(now.size());
for (auto i = 0, l = now.size(); i < l; ++i) {
if (now[i] == ',') {
newText.append('.');
} else {
newText.append(now[i]);
}
}
setCorrectedText(now, nowCursor, newText, newCursor);
}
class Base64UrlInput : public Ui::MaskedInputField {
public:
Base64UrlInput(
@@ -209,7 +251,7 @@ private:
std::shared_ptr<Ui::RadioenumGroup<Type>> _type;
QPointer<Ui::SlideWrap<>> _aboutSponsored;
QPointer<Ui::InputField> _host;
QPointer<HostInput> _host;
QPointer<Ui::PortInput> _port;
QPointer<Ui::InputField> _user;
QPointer<Ui::PasswordInput> _password;
@@ -767,12 +809,12 @@ ProxyBox::ProxyBox(
void ProxyBox::prepare() {
setTitle(tr::lng_proxy_edit());
connect(_host.data(), &Ui::InputField::changed, [=] {
connect(_host.data(), &HostInput::changed, [=] {
Ui::PostponeCall(_host, [=] {
const auto host = _host->getLastText().trimmed();
static const auto mask = u"^\\d+\\.\\d+\\.\\d+\\.\\d+:(\\d*)$"_q;
const auto match = QRegularExpression(mask).match(host);
if (_host->textCursor().position() == host.size()
if (_host->cursorPosition() == host.size()
&& match.hasMatch()) {
const auto port = match.captured(1);
_port->setText(port);
@@ -881,7 +923,7 @@ void ProxyBox::setupSocketAddress(const ProxyData &data) {
_content,
st::connectionHostInputField.heightMin),
st::proxyEditInputPadding);
_host = Ui::CreateChild<Ui::InputField>(
_host = Ui::CreateChild<HostInput>(
address,
st::connectionHostInputField,
tr::lng_connection_host_ph(),
@@ -1043,7 +1085,6 @@ void ProxiesBoxController::ShowApplyConfirmation(
proxy.password = fields.value(qsl("secret"));
}
if (proxy) {
const auto box = std::make_shared<QPointer<ConfirmBox>>();
const auto text = tr::lng_sure_enable_socks(
tr::now,
lt_server,
@@ -1053,7 +1094,7 @@ void ProxiesBoxController::ShowApplyConfirmation(
+ (proxy.type == Type::Mtproto
? "\n\n" + tr::lng_proxy_sponsor_warning(tr::now)
: QString());
*box = Ui::show(Box<ConfirmBox>(text, tr::lng_sure_enable(tr::now), [=] {
auto callback = [=](Fn<void()> &&close) {
auto &proxies = Global::RefProxiesList();
if (!ranges::contains(proxies, proxy)) {
proxies.push_back(proxy);
@@ -1062,10 +1103,14 @@ void ProxiesBoxController::ShowApplyConfirmation(
proxy,
ProxyData::Settings::Enabled);
Local::writeSettings();
if (const auto strong = box->data()) {
strong->closeBox();
}
}), Ui::LayerOption::KeepOther);
close();
};
Ui::show(
Box<ConfirmBox>(
text,
tr::lng_sure_enable(tr::now),
std::move(callback)),
Ui::LayerOption::KeepOther);
} else {
Ui::show(Box<InformBox>(
(proxy.status() == ProxyData::Status::Unsupported

View File

@@ -225,7 +225,7 @@ auto AddButtonWithLoader(
buttonState->value(
) | rpl::start_with_next([=](const DictState &state) {
const auto isToggledSet = state.is<Active>();
const auto isToggledSet = v::is<Active>(state);
const auto toggled = isToggledSet ? 1. : 0.;
const auto over = !button->isDisabled()
&& (button->isDown() || button->isOver());
@@ -252,7 +252,7 @@ auto AddButtonWithLoader(
dictionaryRemoved->events(),
buttonState->value(
) | rpl::filter([](const DictState &state) {
return state.is<Failed>();
return v::is<Failed>(state);
}) | rpl::to_empty
) | rpl::map_to(false)
)
@@ -276,13 +276,14 @@ auto AddButtonWithLoader(
});
}) | rpl::flatten_latest(
) | rpl::filter([=](const DictState &state) {
return !buttonState->current().is<Failed>() || !state.is<Available>();
return !v::is<Failed>(buttonState->current())
|| !v::is<Available>(state);
});
button->toggledValue(
) | rpl::start_with_next([=](bool toggled) {
const auto &state = buttonState->current();
if (toggled && (state.is<Available>() || state.is<Failed>())) {
if (toggled && (v::is<Available>(state) || v::is<Failed>(state))) {
const auto weak = Ui::MakeWeak(button);
setLocalLoader(base::make_unique_q<Loader>(
QCoreApplication::instance(),
@@ -292,7 +293,7 @@ auto AddButtonWithLoader(
Spellchecker::DictPathByLangId(id),
Spellchecker::GetDownloadSize(id),
crl::guard(weak, destroyLocalLoader)));
} else if (!toggled && state.is<Loading>()) {
} else if (!toggled && v::is<Loading>(state)) {
if (const auto g = rawGlobalLoaderPtr()) {
g->destroy();
return;

View File

@@ -34,7 +34,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h"
#include "platform/platform_specific.h"
#include "lang/lang_keys.h"
#include "layout.h"
#include "media/streaming/media_streaming_instance.h"
#include "media/streaming/media_streaming_player.h"
#include "media/streaming/media_streaming_document.h"
@@ -47,6 +46,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/input_fields.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/checkbox.h"
#include "ui/text/format_values.h"
#include "ui/special_buttons.h"
#include "ui/text_options.h"
#include "window/window_session_controller.h"
@@ -431,7 +431,7 @@ void EditCaptionBox::setupStreamedPreview(std::shared_ptr<Document> shared) {
}
void EditCaptionBox::handleStreamingUpdate(Update &&update) {
update.data.match([&](Information &update) {
v::match(update.data, [&](Information &update) {
streamingReady(std::move(update));
}, [&](const PreloadedVideo &update) {
}, [&](const UpdateVideo &update) {
@@ -489,7 +489,7 @@ void EditCaptionBox::updateEditPreview() {
auto isGif = false;
auto shouldAsDoc = true;
auto docPhotoSize = QSize();
if (const auto image = base::get_if<Info::Image>(fileMedia)) {
if (const auto image = std::get_if<Info::Image>(fileMedia)) {
shouldAsDoc = !Storage::ValidateThumbDimensions(
image->data.width(),
image->data.height());
@@ -501,14 +501,14 @@ void EditCaptionBox::updateEditPreview() {
_animated = isGif;
_photo = !isGif && !shouldAsDoc;
_isImage = true;
} else if (const auto video = base::get_if<Info::Video>(fileMedia)) {
} else if (const auto video = std::get_if<Info::Video>(fileMedia)) {
isGif = video->isGifv;
_animated = true;
shouldAsDoc = false;
}
if (shouldAsDoc) {
auto nameString = filename;
if (const auto song = base::get_if<Info::Song>(fileMedia)) {
if (const auto song = std::get_if<Info::Song>(fileMedia)) {
nameString = DocumentData::ComposeNameString(
filename,
song->title,
@@ -684,7 +684,7 @@ bool EditCaptionBox::fileFromClipboard(not_null<const QMimeData*> data) {
const auto imageAsDoc = [&] {
using Info = FileMediaInformation;
const auto fileMedia = &file->information->media;
if (const auto image = base::get_if<Info::Image>(fileMedia)) {
if (const auto image = std::get_if<Info::Image>(fileMedia)) {
return !Storage::ValidateThumbDimensions(
image->data.width(),
image->data.height());
@@ -1030,7 +1030,7 @@ void EditCaptionBox::setName(QString nameString, qint64 size) {
st::semiboldTextStyle,
nameString,
Ui::NameTextOptions());
_status = formatSizeText(size);
_status = Ui::FormatSizeText(size);
}
void EditCaptionBox::keyPressEvent(QKeyEvent *e) {

View File

@@ -86,7 +86,7 @@ void PrivacyExceptionsBoxController::rowClicked(not_null<PeerListRow*> row) {
}
std::unique_ptr<PrivacyExceptionsBoxController::Row> PrivacyExceptionsBoxController::createRow(not_null<History*> history) {
if (history->peer->isSelf()) {
if (history->peer->isSelf() || history->peer->isRepliesChat()) {
return nullptr;
} else if (!history->peer->isUser()
&& !history->peer->isChat()

View File

@@ -241,19 +241,31 @@ void FilterChatsPreview::paintEvent(QPaintEvent *e) {
}
for (auto &[history, userpic, button] : _removePeer) {
const auto savedMessages = history->peer->isSelf();
if (savedMessages) {
Ui::EmptyUserpic::PaintSavedMessages(
p,
iconLeft,
top + iconTop,
width(),
st.photoSize);
const auto repliesMessages = history->peer->isRepliesChat();
if (savedMessages || repliesMessages) {
if (savedMessages) {
Ui::EmptyUserpic::PaintSavedMessages(
p,
iconLeft,
top + iconTop,
width(),
st.photoSize);
} else {
Ui::EmptyUserpic::PaintRepliesMessages(
p,
iconLeft,
top + iconTop,
width(),
st.photoSize);
}
p.setPen(st::contactsNameFg);
p.drawTextLeft(
nameLeft,
top + nameTop,
width(),
tr::lng_saved_messages(tr::now));
(savedMessages
? tr::lng_saved_messages(tr::now)
: tr::lng_replies_messages(tr::now)));
} else {
history->peer->paintUserpicLeft(
p,

View File

@@ -170,6 +170,8 @@ ExceptionRow::ExceptionRow(not_null<History*> history) : Row(history) {
QString ExceptionRow::generateName() {
return peer()->isSelf()
? tr::lng_saved_messages(tr::now)
: peer()->isRepliesChat()
? tr::lng_replies_messages(tr::now)
: Row::generateName();
}
@@ -180,10 +182,13 @@ QString ExceptionRow::generateShortName() {
PaintRoundImageCallback ExceptionRow::generatePaintUserpicCallback() {
const auto peer = this->peer();
const auto saved = peer->isSelf();
const auto replies = peer->isRepliesChat();
auto userpic = saved ? nullptr : ensureUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
if (saved) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
} else if (replies) {
Ui::EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
} else {
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
}

View File

@@ -105,7 +105,7 @@ private:
return (index == other.index);
}
};
using Selection = base::optional_variant<RowSelection, MenuSelection>;
using Selection = std::variant<v::null_t, RowSelection, MenuSelection>;
void updateSelected(Selection selected);
void updatePressed(Selection pressed);
@@ -196,7 +196,7 @@ std::pair<Languages, Languages> PrepareLists() {
const auto projId = [](const Language &language) {
return language.id;
};
const auto current = Lang::LanguageIdOrDefault(Lang::Current().id());
const auto current = Lang::LanguageIdOrDefault(Lang::Id());
auto official = Lang::CurrentCloudManager().languageList();
auto recent = Local::readRecentLanguages();
ranges::stable_partition(recent, [&](const Language &language) {
@@ -207,13 +207,13 @@ std::pair<Languages, Languages> PrepareLists() {
const auto generate = [&] {
const auto name = (current == "#custom")
? "Custom lang pack"
: Lang::Current().name();
: Lang::GetInstance().name();
return Language{
current,
QString(),
QString(),
name,
Lang::Current().nativeName()
Lang::GetInstance().nativeName()
};
};
const auto i = ranges::find(official, current, projId);
@@ -327,7 +327,7 @@ void Rows::mouseMoveEvent(QMouseEvent *e) {
void Rows::mousePressEvent(QMouseEvent *e) {
updatePressed(_selected);
if (_pressed.has_value()
if (!v::is_null(_pressed)
&& !rowBySelection(_pressed).menuToggleForceRippled) {
addRipple(_pressed, e->pos());
}
@@ -348,11 +348,11 @@ QRect Rows::menuToggleArea(not_null<const Row*> row) const {
}
void Rows::addRipple(Selection selected, QPoint position) {
Expects(selected.has_value());
Expects(!v::is_null(selected));
ensureRippleBySelection(selected);
const auto menu = selected.is<MenuSelection>();
const auto menu = v::is<MenuSelection>(selected);
const auto &row = rowBySelection(selected);
const auto menuArea = menuToggleArea(&row);
auto &ripple = rippleBySelection(&row, selected);
@@ -369,7 +369,7 @@ void Rows::ensureRippleBySelection(not_null<Row*> row, Selection selected) {
if (ripple) {
return;
}
const auto menu = selected.is<MenuSelection>();
const auto menu = v::is<MenuSelection>(selected);
const auto menuArea = menuToggleArea(row);
auto mask = menu
? Ui::RippleAnimation::ellipseMask(menuArea.size())
@@ -391,11 +391,11 @@ void Rows::mouseReleaseEvent(QMouseEvent *e) {
const auto pressed = _pressed;
updatePressed({});
if (pressed == _selected) {
pressed.match([&](RowSelection data) {
v::match(pressed, [&](RowSelection data) {
activateByIndex(data.index);
}, [&](MenuSelection data) {
showMenu(data.index);
}, [](std::nullopt_t) {});
}, [](v::null_t) {});
}
}
@@ -597,11 +597,11 @@ int Rows::count() const {
}
int Rows::indexFromSelection(Selection selected) const {
return selected.match([&](RowSelection data) {
return v::match(selected, [&](RowSelection data) {
return data.index;
}, [&](MenuSelection data) {
return data.index;
}, [](std::nullopt_t) {
}, [](v::null_t) {
return -1;
});
}
@@ -648,7 +648,7 @@ rpl::producer<bool> Rows::isEmpty() const {
}
void Rows::repaint(Selection selected) {
selected.match([](std::nullopt_t) {
v::match(selected, [](v::null_t) {
}, [&](const auto &data) {
repaint(data.index);
});
@@ -672,17 +672,17 @@ void Rows::repaintChecked(not_null<const Row*> row) {
}
void Rows::updateSelected(Selection selected) {
const auto changed = (_selected.has_value() != selected.has_value());
const auto changed = (v::is_null(_selected) != v::is_null(selected));
repaint(_selected);
_selected = selected;
repaint(_selected);
if (changed) {
_hasSelection.fire(_selected.has_value());
_hasSelection.fire(!v::is_null(_selected));
}
}
void Rows::updatePressed(Selection pressed) {
if (_pressed.has_value()) {
if (!v::is_null(_pressed)) {
if (!rowBySelection(_pressed).menuToggleForceRippled) {
if (const auto ripple = rippleBySelection(_pressed).get()) {
ripple->lastStop();
@@ -725,7 +725,7 @@ const std::unique_ptr<Ui::RippleAnimation> &Rows::rippleBySelection(
std::unique_ptr<Ui::RippleAnimation> &Rows::rippleBySelection(
not_null<Row*> row,
Selection selected) {
return selected.is<MenuSelection>()
return v::is<MenuSelection>(selected)
? row->menuToggleRipple
: row->ripple;
}
@@ -796,7 +796,7 @@ void Rows::paintEvent(QPaintEvent *e) {
const auto menu = menuToggleArea();
const auto selectedIndex = (_menuShownIndex >= 0)
? _menuShownIndex
: indexFromSelection(_pressed.has_value() ? _pressed : _selected);
: indexFromSelection(!v::is_null(_pressed) ? _pressed : _selected);
for (auto i = 0, till = count(); i != till; ++i) {
const auto &row = rowByIndex(i);
if (row.top + row.height <= clip.y()) {
@@ -867,7 +867,7 @@ void Content::setupContent(
const Languages &official) {
using namespace rpl::mappers;
const auto current = Lang::LanguageIdOrDefault(Lang::Current().id());
const auto current = Lang::LanguageIdOrDefault(Lang::Id());
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
const auto add = [&](const Languages &list, bool areOfficial) {
if (list.empty()) {
@@ -1101,7 +1101,7 @@ void LanguageBox::prepare() {
// "#custom" is applied each time it's passed to switchToLanguage().
// So we check that the language really has changed.
const auto currentId = [] {
return Lang::LanguageIdOrDefault(Lang::Current().id());
return Lang::LanguageIdOrDefault(Lang::Id());
};
if (language.id != currentId()) {
Lang::CurrentCloudManager().switchToLanguage(language);

View File

@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/shadow.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/effects/radial_animation.h"
#include "ui/text/format_values.h"
#include "ui/emoji_config.h"
#include "storage/storage_account.h"
#include "storage/cache/storage_cache_database.h"
@@ -21,7 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "mainwindow.h"
#include "main/main_session.h"
#include "layout.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -260,7 +260,7 @@ QString LocalStorageBox::Row::titleText(const Database::TaggedSummary &data) con
QString LocalStorageBox::Row::sizeText(const Database::TaggedSummary &data) const {
return data.totalSize
? formatSizeText(data.totalSize)
? Ui::FormatSizeText(data.totalSize)
: tr::lng_local_storage_empty(tr::now);
}

View File

@@ -17,6 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_domain.h"
#include "core/application.h"
#include "storage/storage_domain.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
@@ -24,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/fade_wrap.h"
#include "passport/passport_encryption.h"
#include "passport/passport_panel_edit_contact.h"
#include "settings/settings_privacy_security.h"
#include "facades.h"
#include "styles/style_layers.h"
#include "styles/style_passport.h"
@@ -31,6 +34,68 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
enum class PasswordErrorType {
None,
NoPassword,
Later,
};
void SetCloudPassword(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session) {
session->api().passwordState(
) | rpl::start_with_next([=] {
using namespace Settings;
const auto weak = Ui::MakeWeak(box);
if (CheckEditCloudPassword(session)) {
box->getDelegate()->show(
EditCloudPasswordBox(session));
} else {
box->getDelegate()->show(CloudPasswordAppOutdatedBox());
}
if (weak) {
weak->closeBox();
}
}, box->lifetime());
}
void TransferPasswordError(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session,
TextWithEntities &&about,
PasswordErrorType error) {
box->setTitle(tr::lng_rights_transfer_check());
box->setWidth(st::transferCheckWidth);
auto text = std::move(about).append('\n').append('\n').append(
tr::lng_rights_transfer_check_password(
tr::now,
Ui::Text::RichLangValue)
).append('\n').append('\n').append(
tr::lng_rights_transfer_check_session(
tr::now,
Ui::Text::RichLangValue)
);
if (error == PasswordErrorType::Later) {
text.append('\n').append('\n').append(
tr::lng_rights_transfer_check_later(
tr::now,
Ui::Text::RichLangValue));
}
box->addRow(object_ptr<Ui::FlatLabel>(
box,
rpl::single(text),
st::boxLabel));
if (error == PasswordErrorType::Later) {
box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); });
} else {
box->addButton(tr::lng_rights_transfer_set_password(), [=] {
SetCloudPassword(box, session);
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
}
} // namespace
PasscodeBox::CloudFields PasscodeBox::CloudFields::From(
@@ -537,14 +602,11 @@ void PasscodeBox::submitOnlyCheckCloudPassword(const QString &oldPassword) {
if (_cloudFields.turningOff && _cloudFields.notEmptyPassport) {
Assert(!_cloudFields.customCheckCallback);
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
const auto confirmed = [=] {
const auto confirmed = [=](Fn<void()> &&close) {
send();
if (*box) {
(*box)->closeBox();
}
close();
};
*box = getDelegate()->show(Box<ConfirmBox>(
getDelegate()->show(Box<ConfirmBox>(
tr::lng_cloud_password_passport_losing(tr::now),
tr::lng_continue(tr::now),
confirmed));
@@ -744,20 +806,16 @@ void PasscodeBox::changeCloudPassword(
}
void PasscodeBox::suggestSecretReset(const QString &newPassword) {
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
const auto resetSecretAndSave = [=] {
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
resetSecret(check, newPassword, [=] {
if (*box) {
(*box)->closeBox();
}
});
auto resetSecretAndSave = [=](Fn<void()> &&close) {
checkPasswordHash([=, close = std::move(close)](
const Core::CloudPasswordResult &check) {
resetSecret(check, newPassword, std::move(close));
});
};
*box = getDelegate()->show(Box<ConfirmBox>(
getDelegate()->show(Box<ConfirmBox>(
Lang::Hard::PassportCorruptedChange(),
Lang::Hard::PassportCorruptedReset(),
[=] { resetSecretAndSave(); }));
std::move(resetSecretAndSave)));
}
void PasscodeBox::resetSecret(
@@ -1002,14 +1060,11 @@ void RecoverBox::submit() {
}).handleFloodErrors().send();
});
if (_notEmptyPassport) {
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
const auto confirmed = [=] {
const auto confirmed = [=](Fn<void()> &&close) {
send();
if (*box) {
(*box)->closeBox();
}
close();
};
*box = getDelegate()->show(Box<ConfirmBox>(
getDelegate()->show(Box<ConfirmBox>(
tr::lng_cloud_password_passport_losing(tr::now),
tr::lng_continue(tr::now),
confirmed));
@@ -1139,3 +1194,28 @@ RecoveryEmailValidation ConfirmRecoveryEmail(
*weak = box.data();
return { std::move(box), reloads->events(), cancels->events() };
}
[[nodiscard]] object_ptr<Ui::GenericBox> PrePasswordErrorBox(
const RPCError &error,
not_null<Main::Session*> session,
TextWithEntities &&about) {
const auto type = [&] {
const auto &type = error.type();
if (type == qstr("PASSWORD_MISSING")) {
return PasswordErrorType::NoPassword;
} else if (type.startsWith(qstr("PASSWORD_TOO_FRESH_"))
|| type.startsWith(qstr("SESSION_TOO_FRESH_"))) {
return PasswordErrorType::Later;
}
return PasswordErrorType::None;
}();
if (type == PasswordErrorType::None) {
return nullptr;
}
return Box(
TransferPasswordError,
session,
std::move(about),
type);
}

View File

@@ -214,3 +214,8 @@ struct RecoveryEmailValidation {
[[nodiscard]] RecoveryEmailValidation ConfirmRecoveryEmail(
not_null<Main::Session*> session,
const QString &pattern);
[[nodiscard]] object_ptr<Ui::GenericBox> PrePasswordErrorBox(
const RPCError &error,
not_null<Main::Session*> session,
TextWithEntities &&about);

View File

@@ -36,10 +36,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
PaintRoundImageCallback PaintUserpicCallback(
not_null<PeerData*> peer,
bool respectSavedMessagesChat) {
if (respectSavedMessagesChat && peer->isSelf()) {
return [](Painter &p, int x, int y, int outerWidth, int size) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
};
if (respectSavedMessagesChat) {
if (peer->isSelf()) {
return [](Painter &p, int x, int y, int outerWidth, int size) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
};
} else if (peer->isRepliesChat()) {
return [](Painter &p, int x, int y, int outerWidth, int size) {
Ui::EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
};
}
}
auto userpic = std::shared_ptr<Data::CloudImageView>();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
@@ -318,6 +324,8 @@ void PeerListBox::addSelectItem(
const auto respect = _controller->respectSavedMessagesChat();
const auto text = (respect && peer->isSelf())
? tr::lng_saved_short(tr::now)
: (respect && peer->isRepliesChat())
? tr::lng_replies_messages(tr::now)
: peer->shortName();
addSelectItem(
peer->id,
@@ -400,14 +408,16 @@ PeerListRow::PeerListRow(not_null<PeerData*> peer, PeerListRowId id)
, _peer(peer)
, _initialized(false)
, _isSearchResult(false)
, _isSavedMessagesChat(false) {
, _isSavedMessagesChat(false)
, _isRepliesMessagesChat(false) {
}
PeerListRow::PeerListRow(PeerListRowId id)
: _id(id)
, _initialized(false)
, _isSearchResult(false)
, _isSavedMessagesChat(false) {
, _isSavedMessagesChat(false)
, _isRepliesMessagesChat(false) {
}
PeerListRow::~PeerListRow() = default;
@@ -470,6 +480,8 @@ void PeerListRow::refreshName(const style::PeerListItem &st) {
}
const auto text = _isSavedMessagesChat
? tr::lng_saved_messages(tr::now)
: _isRepliesMessagesChat
? tr::lng_replies_messages(tr::now)
: generateName();
_name.setText(st.nameStyle, text, Ui::NameTextOptions());
}
@@ -481,6 +493,8 @@ QString PeerListRow::generateName() {
QString PeerListRow::generateShortName() {
return _isSavedMessagesChat
? tr::lng_saved_short(tr::now)
: _isRepliesMessagesChat
? tr::lng_replies_messages(tr::now)
: peer()->shortName();
}
@@ -493,11 +507,14 @@ std::shared_ptr<Data::CloudImageView> PeerListRow::ensureUserpicView() {
PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback() {
const auto saved = _isSavedMessagesChat;
const auto replies = _isRepliesMessagesChat;
const auto peer = this->peer();
auto userpic = saved ? nullptr : ensureUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
if (saved) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
} else if (replies) {
Ui::EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
} else {
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
}
@@ -603,6 +620,8 @@ void PeerListRow::paintDisabledCheckUserpic(
if (_isSavedMessagesChat) {
Ui::EmptyUserpic::PaintSavedMessages(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
} else if (_isRepliesMessagesChat) {
Ui::EmptyUserpic::PaintRepliesMessages(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
} else {
peer()->paintUserpicLeft(p, _userpic, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
}
@@ -731,10 +750,12 @@ void PeerListContent::changeCheckState(
}
void PeerListContent::addRowEntry(not_null<PeerListRow*> row) {
if (_controller->respectSavedMessagesChat()
&& !row->special()
&& row->peer()->isSelf()) {
row->setIsSavedMessagesChat(true);
if (_controller->respectSavedMessagesChat() && !row->special()) {
if (row->peer()->isSelf()) {
row->setIsSavedMessagesChat(true);
} else if (row->peer()->isRepliesChat()) {
row->setIsRepliesMessagesChat(true);
}
}
_rowsById.emplace(row->id(), row);
if (!row->special()) {

View File

@@ -146,12 +146,12 @@ public:
void setIsSearchResult(bool isSearchResult) {
_isSearchResult = isSearchResult;
}
bool isSavedMessagesChat() const {
return _isSavedMessagesChat;
}
void setIsSavedMessagesChat(bool isSavedMessagesChat) {
_isSavedMessagesChat = isSavedMessagesChat;
}
void setIsRepliesMessagesChat(bool isRepliesMessagesChat) {
_isRepliesMessagesChat = isRepliesMessagesChat;
}
template <typename UpdateCallback>
void setChecked(
@@ -234,6 +234,7 @@ private:
bool _initialized : 1;
bool _isSearchResult : 1;
bool _isSavedMessagesChat : 1;
bool _isRepliesMessagesChat : 1;
};

View File

@@ -584,8 +584,7 @@ void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
auto ChooseRecipientBoxController::createRow(
not_null<History*> history) -> std::unique_ptr<Row> {
const auto peer = history->peer;
const auto skip = peer->isChannel()
&& !peer->isMegagroup()
&& !peer->canWrite();
const auto skip = (peer->isBroadcast() && !peer->canWrite())
|| peer->isRepliesChat();
return skip ? nullptr : std::make_unique<Row>(history);
}

View File

@@ -203,8 +203,7 @@ public:
protected:
void prepareViewHook() override;
std::unique_ptr<Row> createRow(
not_null<History*> history) override;
std::unique_ptr<Row> createRow(not_null<History*> history) override;
private:
const not_null<Window::SessionNavigation*> _navigation;

View File

@@ -576,11 +576,7 @@ void AddSpecialBoxController::showAdmin(
}
// Finally show the admin.
const auto currentRights = _additional.isCreator(user)
? MTPChatAdminRights(MTP_chatAdminRights(
MTP_flags(~MTPDchatAdminRights::Flag::f_add_admins
| MTPDchatAdminRights::Flag::f_add_admins)))
: adminRights
const auto currentRights = adminRights
? *adminRights
: MTPChatAdminRights(MTP_chatAdminRights(MTP_flags(0)));
auto box = Box<EditAdminBox>(
@@ -618,6 +614,7 @@ void AddSpecialBoxController::editAdminDone(
_additional.applyParticipant(MTP_channelParticipantCreator(
MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank),
MTP_int(user->bareId()),
rights,
MTP_string(rank)));
} else if (rights.c_chatAdminRights().vflags().v == 0) {
_additional.applyParticipant(MTP_channelParticipant(
@@ -665,7 +662,7 @@ void AddSpecialBoxController::showRestricted(
} else if (_additional.adminRights(user).has_value()
|| _additional.isCreator(user)) {
// The user is an admin or creator.
if (_additional.canEditAdmin(user)) {
if (!_additional.isCreator(user) && _additional.canEditAdmin(user)) {
if (!sure) {
_editBox = Ui::show(
Box<ConfirmBox>(
@@ -755,7 +752,7 @@ void AddSpecialBoxController::kickUser(
if (_additional.adminRights(user).has_value()
|| _additional.isCreator(user)) {
// The user is an admin or creator.
if (_additional.canEditAdmin(user)) {
if (!_additional.isCreator(user) && _additional.canEditAdmin(user)) {
if (!sure) {
_editBox = Ui::show(
Box<ConfirmBox>(

View File

@@ -146,15 +146,12 @@ void Controller::choose(not_null<ChannelData*> chat) {
Ui::Text::RichLangValue));
}
}
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
const auto sure = [=] {
if (*box) {
(*box)->closeBox();
}
const auto sure = [=](Fn<void()> &&close) {
close();
const auto onstack = _callback;
onstack(chat);
};
*box = Ui::show(
Ui::show(
Box<ConfirmBox>(
text,
tr::lng_manage_discussion_group_link(tr::now),
@@ -178,18 +175,15 @@ void Controller::choose(not_null<ChatData*> chat) {
text.append(tr::lng_manage_discussion_group_warning(
tr::now,
Ui::Text::RichLangValue));
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
const auto sure = [=] {
if (*box) {
(*box)->closeBox();
}
const auto sure = [=](Fn<void()> &&close) {
close();
const auto done = [=](not_null<ChannelData*> chat) {
const auto onstack = _callback;
onstack(chat);
};
chat->session().api().migrateChat(chat, crl::guard(this, done));
};
*box = Ui::show(
Ui::show(
Box<ConfirmBox>(
text,
tr::lng_manage_discussion_group_link(tr::now),

View File

@@ -47,70 +47,6 @@ constexpr auto kSecondsInDay = 24 * 60 * 60;
constexpr auto kSecondsInWeek = 7 * kSecondsInDay;
constexpr auto kAdminRoleLimit = 16;
enum class PasswordErrorType {
None,
NoPassword,
Later,
};
void SetCloudPassword(not_null<Ui::GenericBox*> box, not_null<UserData*> user) {
user->session().api().passwordState(
) | rpl::start_with_next([=] {
using namespace Settings;
const auto weak = Ui::MakeWeak(box);
if (CheckEditCloudPassword(&user->session())) {
box->getDelegate()->show(
EditCloudPasswordBox(&user->session()));
} else {
box->getDelegate()->show(CloudPasswordAppOutdatedBox());
}
if (weak) {
weak->closeBox();
}
}, box->lifetime());
}
void TransferPasswordError(
not_null<Ui::GenericBox*> box,
not_null<UserData*> user,
PasswordErrorType error) {
box->setTitle(tr::lng_rights_transfer_check());
box->setWidth(st::transferCheckWidth);
auto text = tr::lng_rights_transfer_check_about(
tr::now,
lt_user,
Ui::Text::Bold(user->shortName()),
Ui::Text::WithEntities
).append('\n').append('\n').append(
tr::lng_rights_transfer_check_password(
tr::now,
Ui::Text::RichLangValue)
).append('\n').append('\n').append(
tr::lng_rights_transfer_check_session(
tr::now,
Ui::Text::RichLangValue)
);
if (error == PasswordErrorType::Later) {
text.append('\n').append('\n').append(
tr::lng_rights_transfer_check_later(
tr::now,
Ui::Text::RichLangValue));
}
box->addRow(object_ptr<Ui::FlatLabel>(
box,
rpl::single(text),
st::boxLabel));
if (error == PasswordErrorType::Later) {
box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); });
} else {
box->addButton(tr::lng_rights_transfer_set_password(), [=] {
SetCloudPassword(box, user);
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
}
} // namespace
class EditParticipantBox::Inner : public Ui::RpWidget {
@@ -318,7 +254,7 @@ void EditAdminBox::prepare() {
const auto disabledMessages = [&] {
auto result = std::map<Flags, QString>();
if (!canSave() || (amCreator() && user()->isSelf())) {
if (!canSave()) {
result.emplace(
~Flags(0),
tr::lng_rights_about_admin_cant_edit(tr::now));
@@ -327,7 +263,11 @@ void EditAdminBox::prepare() {
disabledByDefaults,
tr::lng_rights_permission_for_all(tr::now));
if (const auto channel = peer()->asChannel()) {
if (!channel->amCreator()) {
if (amCreator() && user()->isSelf()) {
result.emplace(
~Flag::f_anonymous,
tr::lng_rights_permission_cant_edit(tr::now));
} else if (!channel->amCreator()) {
result.emplace(
~channel->adminRights(),
tr::lng_rights_permission_cant_edit(tr::now));
@@ -520,22 +460,17 @@ void EditAdminBox::transferOwnership() {
}
bool EditAdminBox::handleTransferPasswordError(const RPCError &error) {
const auto type = [&] {
const auto &type = error.type();
if (type == qstr("PASSWORD_MISSING")) {
return PasswordErrorType::NoPassword;
} else if (type.startsWith(qstr("PASSWORD_TOO_FRESH_"))
|| type.startsWith(qstr("SESSION_TOO_FRESH_"))) {
return PasswordErrorType::Later;
}
return PasswordErrorType::None;
}();
if (type == PasswordErrorType::None) {
return false;
const auto session = &user()->session();
auto about = tr::lng_rights_transfer_check_about(
tr::now,
lt_user,
Ui::Text::Bold(user()->shortName()),
Ui::Text::WithEntities);
if (auto box = PrePasswordErrorBox(error, session, std::move(about))) {
getDelegate()->show(std::move(box));
return true;
}
getDelegate()->show(Box(TransferPasswordError, user(), type));
return true;
return false;
}
void EditAdminBox::transferOwnershipChecked() {
@@ -639,7 +574,9 @@ void EditAdminBox::sendTransferRequestFrom(
void EditAdminBox::refreshAboutAddAdminsText(bool canAddAdmins) {
_aboutAddAdmins->setText([&] {
if (!canSave() || (amCreator() && user()->isSelf())) {
if (amCreator() && user()->isSelf()) {
return QString();
} else if (!canSave()) {
return tr::lng_rights_about_admin_cant_edit(tr::now);
} else if (canAddAdmins) {
return tr::lng_rights_about_add_admins_yes(tr::now);

View File

@@ -564,6 +564,12 @@ UserData *ParticipantsAdditionalData::applyCreator(
const MTPDchannelParticipantCreator &data) {
if (const auto user = applyRegular(data.vuser_id())) {
_creator = user;
_adminRights[user] = data.vadmin_rights();
if (user->isSelf()) {
_adminCanEdit.emplace(user);
} else {
_adminCanEdit.erase(user);
}
if (const auto rank = data.vrank()) {
_adminRanks[user] = qs(*rank);
} else {
@@ -1459,11 +1465,7 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
void ParticipantsBoxController::showAdmin(not_null<UserData*> user) {
const auto adminRights = _additional.adminRights(user);
const auto currentRights = _additional.isCreator(user)
? MTPChatAdminRights(MTP_chatAdminRights(
MTP_flags(~MTPDchatAdminRights::Flag::f_add_admins
| MTPDchatAdminRights::Flag::f_add_admins)))
: adminRights
const auto currentRights = adminRights
? *adminRights
: MTPChatAdminRights(MTP_chatAdminRights(MTP_flags(0)));
auto box = Box<EditAdminBox>(
@@ -1504,6 +1506,7 @@ void ParticipantsBoxController::editAdminDone(
_additional.applyParticipant(MTP_channelParticipantCreator(
MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank),
MTP_int(user->bareId()),
rights,
MTP_string(rank)));
} else if (rights.c_chatAdminRights().vflags().v == 0) {
_additional.applyParticipant(MTP_channelParticipant(
@@ -1783,6 +1786,7 @@ std::unique_ptr<PeerListRow> ParticipantsBoxController::createRow(
auto row = std::make_unique<PeerListRowWithLink>(user);
refreshCustomStatus(row.get());
if (_role == Role::Admins
&& !_additional.isCreator(user)
&& _additional.adminRights(user).has_value()
&& _additional.canEditAdmin(user)) {
row->setActionLink(tr::lng_profile_kick(tr::now));

View File

@@ -135,6 +135,7 @@ std::vector<std::pair<ChatAdminRights, QString>> AdminRightLabels(
? tr::lng_rights_group_invite_link(tr::now)
: tr::lng_rights_group_invite(tr::now) },
{ Flag::f_pin_messages, tr::lng_rights_group_pin(tr::now) },
{ Flag::f_anonymous, tr::lng_rights_group_anonymous(tr::now) },
{ Flag::f_add_admins, tr::lng_rights_add_admins(tr::now) },
};
} else {

View File

@@ -546,17 +546,14 @@ void Controller::revokeInviteLink() {
}
void Controller::exportInviteLink(const QString &confirmation) {
const auto boxPointer = std::make_shared<QPointer<ConfirmBox>>();
const auto callback = crl::guard(this, [=] {
if (const auto strong = *boxPointer) {
strong->closeBox();
}
const auto callback = crl::guard(this, [=](Fn<void()> &&close) {
close();
_peer->session().api().exportInviteLink(_peer->migrateToOrMe());
});
auto box = Box<ConfirmBox>(
confirmation,
std::move(callback));
*boxPointer = Ui::show(std::move(box), Ui::LayerOption::KeepOther);
Ui::show(std::move(box), Ui::LayerOption::KeepOther);
}
bool Controller::canEditInviteLink() const {

View File

@@ -9,8 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "boxes/confirm_box.h"
#include "history/history_item.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
@@ -18,6 +20,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "core/core_settings.h"
#include "core/application.h"
#include "window/window_session_controller.h"
#include "window/window_peer_menu.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_profile.h"
@@ -207,3 +211,18 @@ void ReportBox::updateMaxHeight() {
}
setDimensions(st::boxWidth, newHeight);
}
void BlockSenderFromRepliesBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> controller,
FullMsgId id) {
const auto item = controller->session().data().message(id);
Assert(item != nullptr);
PeerMenuBlockUserBox(
box,
&controller->window(),
item->senderOriginal(),
true,
Window::ClearReply{ id });
}

View File

@@ -8,8 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "boxes/abstract_box.h"
#include "ui/layers/generic_box.h"
#include "mtproto/sender.h"
namespace Window {
class SessionController;
} // namespace Window
namespace Ui {
template <typename Enum>
class RadioenumGroup;
@@ -60,3 +65,8 @@ private:
mtpRequestId _requestId = 0;
};
void BlockSenderFromRepliesBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> controller,
FullMsgId id);

View File

@@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/input_fields.h"
#include "ui/widgets/scroll_area.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/text/format_values.h"
#include "ui/grouped_layout.h"
#include "ui/text_options.h"
#include "ui/special_buttons.h"
@@ -41,7 +42,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "layout.h"
#include "facades.h" // App::LambdaDelayed.
#include "app.h"
#include "styles/style_history.h"
@@ -352,7 +352,7 @@ AlbumThumb::AlbumThumb(
} else {
auto fileinfo = QFileInfo(filepath);
_name = fileinfo.fileName();
_status = formatSizeText(fileinfo.size());
_status = Ui::FormatSizeText(fileinfo.size());
}
_nameWidth = st::semiboldFont->width(_name);
if (_nameWidth > availableFileWidth) {
@@ -766,11 +766,11 @@ SingleMediaPreview *SingleMediaPreview::Create(
auto preview = QImage();
bool animated = false;
bool animationPreview = false;
if (const auto image = base::get_if<FileMediaInformation::Image>(
if (const auto image = std::get_if<FileMediaInformation::Image>(
&file.information->media)) {
preview = image->data;
animated = animationPreview = image->animated;
} else if (const auto video = base::get_if<FileMediaInformation::Video>(
} else if (const auto video = std::get_if<FileMediaInformation::Video>(
&file.information->media)) {
preview = video->thumbnail;
animated = true;
@@ -1004,10 +1004,10 @@ void SingleFilePreview::prepareThumb(const QImage &preview) {
void SingleFilePreview::preparePreview(const Storage::PreparedFile &file) {
auto preview = QImage();
if (const auto image = base::get_if<FileMediaInformation::Image>(
if (const auto image = std::get_if<FileMediaInformation::Image>(
&file.information->media)) {
preview = image->data;
} else if (const auto video = base::get_if<FileMediaInformation::Video>(
} else if (const auto video = std::get_if<FileMediaInformation::Video>(
&file.information->media)) {
preview = video->thumbnail;
}
@@ -1034,7 +1034,7 @@ void SingleFilePreview::preparePreview(const Storage::PreparedFile &file) {
auto songTitle = QString();
auto songPerformer = QString();
if (file.information) {
if (const auto song = base::get_if<FileMediaInformation::Song>(
if (const auto song = std::get_if<FileMediaInformation::Song>(
&file.information->media)) {
songTitle = song->title;
songPerformer = song->performer;
@@ -1050,7 +1050,7 @@ void SingleFilePreview::preparePreview(const Storage::PreparedFile &file) {
st::semiboldTextStyle,
nameString,
Ui::NameTextOptions());
_statusText = formatSizeText(fileinfo.size());
_statusText = Ui::FormatSizeText(fileinfo.size());
_statusWidth = qMax(
_nameText.maxWidth(),
st::normalFont->width(_statusText));

View File

@@ -7,24 +7,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/sessions_box.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "apiwrap.h"
#include "api/api_authorizations.h"
#include "base/timer.h"
#include "base/unixtime.h"
#include "boxes/confirm_box.h"
#include "settings/settings_common.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "styles/style_layers.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"
#include "styles/style_layers.h"
#include "styles/style_settings.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/scroll_area.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "window/window_session_controller.h"
namespace {
@@ -32,7 +31,63 @@ constexpr auto kSessionsShortPollTimeout = 60 * crl::time(1000);
} // namespace
class SessionsBox::List : public Ui::RpWidget {
class SessionsContent : public Ui::RpWidget {
public:
SessionsContent(QWidget*, not_null<Main::Session*> session);
void setupContent();
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private:
struct Entry {
Entry() = default;
Entry(const Api::Authorizations::Entry &entry)
: hash(entry.hash)
, incomplete(entry.incomplete)
, activeTime(entry.activeTime)
, name(st::sessionNameStyle, entry.name)
, active(st::sessionWhenStyle, entry.active)
, info(st::sessionInfoStyle, entry.info)
, ip(st::sessionInfoStyle, entry.ip) {
};
uint64 hash = 0;
bool incomplete = false;
TimeId activeTime = 0;
Ui::Text::String name, active, info, ip;
};
struct Full {
Entry current;
std::vector<Entry> incomplete;
std::vector<Entry> list;
};
class Inner;
class List;
void shortPollSessions();
void parse(const Api::Authorizations::List &list);
void terminate(Fn<void()> terminateRequest, QString message);
void terminateOne(uint64 hash);
void terminateAll();
const not_null<Api::Authorizations*> _authorizations;
rpl::variable<bool> _loading = false;
Full _data;
object_ptr<Inner> _inner;
QPointer<ConfirmBox> _terminateBox;
base::Timer _shortPollTimer;
};
class SessionsContent::List : public Ui::RpWidget {
public:
List(QWidget *parent);
@@ -43,11 +98,20 @@ public:
void terminating(uint64 hash, bool terminating);
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
int resizeGetHeight(int newWidth) override;
private:
struct RowWidth {
int available = 0;
int info = 0;
};
RowWidth _rowWidth;
void computeRowWidth();
std::vector<Entry> _items;
std::map<uint64, std::unique_ptr<Ui::IconButton>> _terminateButtons;
rpl::event_stream<uint64> _terminate;
@@ -55,7 +119,7 @@ private:
};
class SessionsBox::Inner : public Ui::RpWidget {
class SessionsContent::Inner : public Ui::RpWidget {
public:
Inner(QWidget *parent);
@@ -75,22 +139,21 @@ private:
};
SessionsBox::SessionsBox(QWidget*, not_null<Main::Session*> session)
: _session(session)
, _api(&_session->mtp())
SessionsContent::SessionsContent(QWidget*, not_null<Main::Session*> session)
: _authorizations(&session->api().authorizations())
, _inner(this)
, _shortPollTimer([=] { shortPollSessions(); }) {
}
void SessionsBox::prepare() {
setTitle(tr::lng_sessions_other_header());
addButton(tr::lng_close(), [=] { closeBox(); });
setDimensions(st::boxWideWidth, st::sessionsHeight);
_inner = setInnerWidget(object_ptr<Inner>(this), st::sessionsScroll);
void SessionsContent::setupContent() {
_inner->resize(width(), st::noContactsHeight);
_inner->heightValue(
) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](int height) {
resize(width(), height);
}, _inner->lifetime());
_inner->terminateOne(
) | rpl::start_with_next([=](uint64 hash) {
terminateOne(hash);
@@ -101,34 +164,58 @@ void SessionsBox::prepare() {
terminateAll();
}, lifetime());
_session->data().newAuthorizationChecks(
) | rpl::start_with_next([=] {
shortPollSessions();
_loading.changes(
) | rpl::start_with_next([=](bool value) {
_inner->setVisible(!value);
}, lifetime());
setLoading(true);
_authorizations->listChanges(
) | rpl::start_with_next([=](const Api::Authorizations::List &list) {
parse(list);
}, lifetime());
_loading = true;
shortPollSessions();
}
void SessionsBox::setLoading(bool loading) {
if (_loading != loading) {
_loading = loading;
setInnerVisible(!_loading);
void SessionsContent::parse(const Api::Authorizations::List &list) {
if (list.empty()) {
return;
}
_data = Full();
for (const auto &auth : list) {
auto entry = Entry(auth);
if (!entry.hash) {
_data.current = std::move(entry);
} else if (entry.incomplete) {
_data.incomplete.push_back(std::move(entry));
} else {
_data.list.push_back(std::move(entry));
}
}
_loading = false;
ranges::sort(_data.list, std::greater<>(), &Entry::activeTime);
ranges::sort(_data.incomplete, std::greater<>(), &Entry::activeTime);
_inner->showData(_data);
_shortPollTimer.callOnce(kSessionsShortPollTimeout);
}
void SessionsBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
void SessionsContent::resizeEvent(QResizeEvent *e) {
RpWidget::resizeEvent(e);
_inner->resize(width(), _inner->height());
}
void SessionsBox::paintEvent(QPaintEvent *e) {
BoxContent::paintEvent(e);
void SessionsContent::paintEvent(QPaintEvent *e) {
RpWidget::paintEvent(e);
Painter p(this);
if (_loading) {
if (_loading.current()) {
p.setFont(st::noContactsFont);
p.setPen(st::noContactsColor);
p.drawText(
@@ -138,166 +225,43 @@ void SessionsBox::paintEvent(QPaintEvent *e) {
}
}
void SessionsBox::got(const MTPaccount_Authorizations &result) {
_shortPollRequest = 0;
setLoading(false);
_data = Full();
result.match([&](const MTPDaccount_authorizations &data) {
const auto &list = data.vauthorizations().v;
for (const auto &auth : list) {
auth.match([&](const MTPDauthorization &data) {
auto entry = ParseEntry(data);
if (!entry.hash) {
_data.current = std::move(entry);
} else if (entry.incomplete) {
_data.incomplete.push_back(std::move(entry));
} else {
_data.list.push_back(std::move(entry));
}
});
}
});
const auto getActiveTime = [](const Entry &entry) {
return entry.activeTime;
};
ranges::sort(_data.list, std::greater<>(), getActiveTime);
ranges::sort(_data.incomplete, std::greater<>(), getActiveTime);
_inner->showData(_data);
_shortPollTimer.callOnce(kSessionsShortPollTimeout);
}
SessionsBox::Entry SessionsBox::ParseEntry(const MTPDauthorization &data) {
auto result = Entry();
result.hash = data.is_current() ? 0 : data.vhash().v;
result.incomplete = data.is_password_pending();
auto appName = QString();
auto appVer = qs(data.vapp_version());
const auto systemVer = qs(data.vsystem_version());
const auto deviceModel = qs(data.vdevice_model());
const auto apiId = data.vapi_id().v;
if (apiId == 2040 || apiId == 17349) {
appName = (apiId == 2040)
? qstr("Telegram Desktop")
: qstr("Telegram Desktop (GitHub)");
//if (systemVer == qstr("windows")) {
// deviceModel = qsl("Windows");
//} else if (systemVer == qstr("os x")) {
// deviceModel = qsl("OS X");
//} else if (systemVer == qstr("linux")) {
// deviceModel = qsl("Linux");
//}
if (appVer == QString::number(appVer.toInt())) {
const auto ver = appVer.toInt();
appVer = QString("%1.%2"
).arg(ver / 1000000
).arg((ver % 1000000) / 1000)
+ ((ver % 1000)
? ('.' + QString::number(ver % 1000))
: QString());
//} else {
// appVer = QString();
}
void SessionsContent::shortPollSessions() {
const auto left = kSessionsShortPollTimeout
- (crl::now() - _authorizations->lastReceivedTime());
if (left > 0) {
parse(_authorizations->list());
_shortPollTimer.cancel();
_shortPollTimer.callOnce(left);
} else {
appName = qs(data.vapp_name());// +qsl(" for ") + qs(d.vplatform());
if (appVer.indexOf('(') >= 0) {
appVer = appVer.mid(appVer.indexOf('('));
}
_authorizations->reload();
}
result.name = appName;
if (!appVer.isEmpty()) {
result.name += ' ' + appVer;
}
const auto country = qs(data.vcountry());
const auto platform = qs(data.vplatform());
//const auto &countries = countriesByISO2();
//const auto j = countries.constFind(country);
//if (j != countries.cend()) {
// country = QString::fromUtf8(j.value()->name);
//}
result.activeTime = data.vdate_active().v
? data.vdate_active().v
: data.vdate_created().v;
result.info = qs(data.vdevice_model()) + qstr(", ") + (platform.isEmpty() ? QString() : platform + ' ') + qs(data.vsystem_version());
result.ip = qs(data.vip()) + (country.isEmpty() ? QString() : QString::fromUtf8(" \xe2\x80\x93 ") + country);
if (!result.hash) {
result.active = tr::lng_status_online(tr::now);
result.activeWidth = st::sessionWhenFont->width(tr::lng_status_online(tr::now));
} else {
const auto now = QDateTime::currentDateTime();
const auto lastTime = base::unixtime::parse(result.activeTime);
const auto nowDate = now.date();
const auto lastDate = lastTime.date();
if (lastDate == nowDate) {
result.active = lastTime.toString(cTimeFormat());
} else if (lastDate.year() == nowDate.year()
&& lastDate.weekNumber() == nowDate.weekNumber()) {
result.active = langDayOfWeek(lastDate);
} else {
result.active = lastDate.toString(qsl("d.MM.yy"));
}
result.activeWidth = st::sessionWhenFont->width(result.active);
}
ResizeEntry(result);
return result;
}
void SessionsBox::ResizeEntry(Entry &entry) {
const auto available = st::boxWideWidth
- st::sessionPadding.left()
- st::sessionTerminateSkip;
const auto availableInList = available
- st::sessionTerminate.iconPosition.x();
const auto availableListInfo = available - st::sessionTerminate.width;
const auto resize = [](
const style::font &font,
QString &string,
int &stringWidth,
int available) {
stringWidth = font->width(string);
if (stringWidth > available) {
string = font->elided(string, available);
stringWidth = font->width(string);
}
};
const auto forName = entry.hash ? availableInList : available;
const auto forInfo = entry.hash ? availableListInfo : available;
resize(st::sessionNameFont, entry.name, entry.nameWidth, forName);
resize(st::sessionInfoFont, entry.info, entry.infoWidth, forInfo);
resize(st::sessionInfoFont, entry.ip, entry.ipWidth, available);
}
void SessionsBox::shortPollSessions() {
if (_shortPollRequest) {
return;
}
_shortPollRequest = _api.request(MTPaccount_GetAuthorizations(
)).done([=](const MTPaccount_Authorizations &result) {
got(result);
}).send();
update();
}
void SessionsBox::terminateOne(uint64 hash) {
if (_terminateBox) _terminateBox->deleteLater();
void SessionsContent::terminate(Fn<void()> terminateRequest, QString message) {
if (_terminateBox) {
_terminateBox->deleteLater();
}
const auto callback = crl::guard(this, [=] {
if (_terminateBox) {
_terminateBox->closeBox();
_terminateBox = nullptr;
}
_api.request(MTPaccount_ResetAuthorization(
MTP_long(hash)
)).done([=](const MTPBool &result) {
terminateRequest();
});
_terminateBox = Ui::show(
Box<ConfirmBox>(
message,
tr::lng_settings_reset_button(tr::now),
st::attentionBoxButton,
callback),
Ui::LayerOption::KeepOther);
}
void SessionsContent::terminateOne(uint64 hash) {
const auto weak = Ui::MakeWeak(this);
auto callback = [=] {
auto done = crl::guard(weak, [=](const MTPBool &result) {
_inner->terminatingOne(hash, false);
const auto getHash = [](const Entry &entry) {
return entry.hash;
@@ -310,52 +274,40 @@ void SessionsBox::terminateOne(uint64 hash) {
removeByHash(_data.incomplete);
removeByHash(_data.list);
_inner->showData(_data);
}).fail([=](const RPCError &error) {
});
auto fail = crl::guard(weak, [=](const RPCError &error) {
_inner->terminatingOne(hash, false);
}).send();
});
_authorizations->requestTerminate(
std::move(done),
std::move(fail),
hash);
_inner->terminatingOne(hash, true);
});
_terminateBox = Ui::show(
Box<ConfirmBox>(
tr::lng_settings_reset_one_sure(tr::now),
tr::lng_settings_reset_button(tr::now),
st::attentionBoxButton,
callback),
Ui::LayerOption::KeepOther);
};
terminate(std::move(callback), tr::lng_settings_reset_one_sure(tr::now));
}
void SessionsBox::terminateAll() {
if (_terminateBox) _terminateBox->deleteLater();
const auto callback = crl::guard(this, [=] {
if (_terminateBox) {
_terminateBox->closeBox();
_terminateBox = nullptr;
}
_api.request(MTPauth_ResetAuthorizations(
)).done([=](const MTPBool &result) {
_api.request(base::take(_shortPollRequest)).cancel();
void SessionsContent::terminateAll() {
const auto weak = Ui::MakeWeak(this);
auto callback = [=] {
const auto reset = crl::guard(weak, [=] {
_authorizations->cancelCurrentRequest();
shortPollSessions();
}).fail([=](const RPCError &result) {
_api.request(base::take(_shortPollRequest)).cancel();
shortPollSessions();
}).send();
setLoading(true);
});
_terminateBox = Ui::show(
Box<ConfirmBox>(
tr::lng_settings_reset_sure(tr::now),
tr::lng_settings_reset_button(tr::now),
st::attentionBoxButton,
callback),
Ui::LayerOption::KeepOther);
});
_authorizations->requestTerminate(
[=](const MTPBool &result) { reset(); },
[=](const RPCError &result) { reset(); });
_loading = true;
};
terminate(std::move(callback), tr::lng_settings_reset_sure(tr::now));
}
SessionsBox::Inner::Inner(QWidget *parent)
SessionsContent::Inner::Inner(QWidget *parent)
: RpWidget(parent) {
setupContent();
}
void SessionsBox::Inner::setupContent() {
void SessionsContent::Inner::setupContent() {
using namespace Settings;
using namespace rpl::mappers;
@@ -418,32 +370,40 @@ void SessionsBox::Inner::setupContent() {
Ui::ResizeFitChild(this, content);
}
void SessionsBox::Inner::showData(const Full &data) {
void SessionsContent::Inner::showData(const Full &data) {
_current->showData({ &data.current, &data.current + 1 });
_list->showData(data.list);
_incomplete->showData(data.incomplete);
}
rpl::producer<> SessionsBox::Inner::terminateAll() const {
rpl::producer<> SessionsContent::Inner::terminateAll() const {
return _terminateAll->clicks() | rpl::to_empty;
}
rpl::producer<uint64> SessionsBox::Inner::terminateOne() const {
rpl::producer<uint64> SessionsContent::Inner::terminateOne() const {
return rpl::merge(
_incomplete->terminate(),
_list->terminate());
}
void SessionsBox::Inner::terminatingOne(uint64 hash, bool terminating) {
void SessionsContent::Inner::terminatingOne(uint64 hash, bool terminating) {
_incomplete->terminating(hash, terminating);
_list->terminating(hash, terminating);
}
SessionsBox::List::List(QWidget *parent) : RpWidget(parent) {
SessionsContent::List::List(QWidget *parent) : RpWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
}
void SessionsBox::List::showData(gsl::span<const Entry> items) {
void SessionsContent::List::resizeEvent(QResizeEvent *e) {
RpWidget::resizeEvent(e);
computeRowWidth();
}
void SessionsContent::List::showData(gsl::span<const Entry> items) {
computeRowWidth();
auto buttons = base::take(_terminateButtons);
_items.clear();
_items.insert(begin(_items), items.begin(), items.end());
@@ -466,24 +426,27 @@ void SessionsBox::List::showData(gsl::span<const Entry> items) {
_terminate.fire_copy(hash);
});
button->show();
button->moveToRight(
st::sessionTerminateSkip,
((_terminateButtons.size() - 1) * st::sessionHeight
+ st::sessionTerminateTop));
const auto number = _terminateButtons.size() - 1;
widthValue(
) | rpl::start_with_next([=] {
button->moveToRight(
st::sessionTerminateSkip,
(number * st::sessionHeight + st::sessionTerminateTop));
}, lifetime());
}
resizeToWidth(width());
_itemsCount.fire(_items.size());
}
rpl::producer<int> SessionsBox::List::itemsCount() const {
rpl::producer<int> SessionsContent::List::itemsCount() const {
return _itemsCount.events_starting_with(_items.size());
}
rpl::producer<uint64> SessionsBox::List::terminate() const {
rpl::producer<uint64> SessionsContent::List::terminate() const {
return _terminate.events();
}
void SessionsBox::List::terminating(uint64 hash, bool terminating) {
void SessionsContent::List::terminating(uint64 hash, bool terminating) {
const auto i = _terminateButtons.find(hash);
if (i != _terminateButtons.cend()) {
if (terminating) {
@@ -495,11 +458,21 @@ void SessionsBox::List::terminating(uint64 hash, bool terminating) {
}
}
int SessionsBox::List::resizeGetHeight(int newWidth) {
int SessionsContent::List::resizeGetHeight(int newWidth) {
return _items.size() * st::sessionHeight;
}
void SessionsBox::List::paintEvent(QPaintEvent *e) {
void SessionsContent::List::computeRowWidth() {
const auto available = width()
- st::sessionPadding.left()
- st::sessionTerminateSkip;
_rowWidth = {
.available = available,
.info = available - st::sessionTerminate.width,
};
}
void SessionsContent::List::paintEvent(QPaintEvent *e) {
QRect r(e->rect());
Painter p(this);
@@ -513,6 +486,7 @@ void SessionsBox::List::paintEvent(QPaintEvent *e) {
0,
count);
const auto available = _rowWidth.available;
const auto x = st::sessionPadding.left();
const auto y = st::sessionPadding.top();
const auto w = width();
@@ -522,23 +496,66 @@ void SessionsBox::List::paintEvent(QPaintEvent *e) {
for (auto i = from; i != till; ++i) {
const auto &entry = _items[i];
p.setFont(st::sessionNameFont);
const auto activeW = entry.active.maxWidth();
const auto nameW = available
- activeW
- st::sessionNamePadding.right();
const auto nameH = entry.name.style()->font->height;
const auto infoW = entry.hash ? _rowWidth.info : available;
const auto infoH = entry.info.style()->font->height;
p.setPen(entry.hash ? st::sessionWhenFg : st::contactsStatusFgOnline);
entry.active.drawRight(p, xact, y, activeW, w);
p.setPen(st::sessionNameFg);
p.drawTextLeft(x, y, w, entry.name, entry.nameWidth);
entry.name.drawLeftElided(p, x, y, nameW, w);
p.setFont(st::sessionWhenFont);
p.setPen(st::sessionWhenFg);
p.drawTextRight(xact, y, w, entry.active, entry.activeWidth);
const auto name = st::sessionNameFont->height;
p.setFont(st::sessionInfoFont);
p.setPen(st::boxTextFg);
p.drawTextLeft(x, y + name, w, entry.info, entry.infoWidth);
entry.info.drawLeftElided(p, x, y + nameH, infoW, w);
const auto info = st::sessionInfoFont->height;
p.setPen(st::sessionInfoFg);
p.drawTextLeft(x, y + name + info, w, entry.ip, entry.ipWidth);
entry.ip.drawLeftElided(p, x, y + nameH + infoH, available, w);
p.translate(0, st::sessionHeight);
}
}
SessionsBox::SessionsBox(QWidget*, not_null<Main::Session*> session)
: _session(session) {
}
void SessionsBox::prepare() {
setTitle(tr::lng_sessions_other_header());
addButton(tr::lng_close(), [=] { closeBox(); });
const auto w = st::boxWideWidth;
const auto content = setInnerWidget(
object_ptr<SessionsContent>(this, _session),
st::sessionsScroll);
content->resize(w, st::noContactsHeight);
content->setupContent();
setDimensions(w, st::sessionsHeight);
}
namespace Settings {
Sessions::Sessions(
QWidget *parent,
not_null<Window::SessionController*> controller)
: Section(parent) {
setupContent(controller);
}
void Sessions::setupContent(not_null<Window::SessionController*> controller) {
const auto container = Ui::CreateChild<Ui::VerticalLayout>(this);
const auto content = container->add(
object_ptr<SessionsContent>(container, &controller->session()));
content->setupContent();
Ui::ResizeFitChild(this, container);
}
} // namespace Settings

View File

@@ -8,20 +8,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "boxes/abstract_box.h"
#include "mtproto/sender.h"
#include "base/timer.h"
class ConfirmBox;
namespace Ui {
class IconButton;
class LinkButton;
} // namespace Ui
#include "settings/settings_common.h"
namespace Main {
class Session;
} // namespace Main
namespace Settings {
class Sessions : public Section {
public:
Sessions(
QWidget *parent,
not_null<Window::SessionController*> controller);
private:
void setupContent(not_null<Window::SessionController*> controller);
};
} // namespace Settings
class SessionsBox : public Ui::BoxContent {
public:
SessionsBox(QWidget*, not_null<Main::Session*> session);
@@ -29,46 +36,7 @@ public:
protected:
void prepare() override;
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private:
struct Entry {
uint64 hash = 0;
bool incomplete = false;
TimeId activeTime = 0;
int nameWidth, activeWidth, infoWidth, ipWidth;
QString name, active, info, ip;
};
struct Full {
Entry current;
std::vector<Entry> incomplete;
std::vector<Entry> list;
};
class Inner;
class List;
static Entry ParseEntry(const MTPDauthorization &data);
static void ResizeEntry(Entry &entry);
void setLoading(bool loading);
void shortPollSessions();
void got(const MTPaccount_Authorizations &result);
void terminateOne(uint64 hash);
void terminateAll();
const not_null<Main::Session*> _session;
MTP::Sender _api;
bool _loading = false;
Full _data;
QPointer<Inner> _inner;
QPointer<ConfirmBox> _terminateBox;
base::Timer _shortPollTimer;
mtpRequestId _shortPollRequest = 0;
};

View File

@@ -635,7 +635,11 @@ void ShareBox::Inner::updateChat(not_null<PeerData*> peer) {
void ShareBox::Inner::updateChatName(
not_null<Chat*> chat,
not_null<PeerData*> peer) {
const auto text = peer->isSelf() ? tr::lng_saved_messages(tr::now) : peer->name;
const auto text = peer->isSelf()
? tr::lng_saved_messages(tr::now)
: peer->isRepliesChat()
? tr::lng_replies_messages(tr::now)
: peer->name;
chat->name.setText(st::shareNameStyle, text, Ui::NameTextOptions());
}

View File

@@ -24,6 +24,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image_location_factory.h"
#include "ui/text/text_utilities.h"
#include "ui/emoji_config.h"
#include "ui/toast/toast.h"
#include "ui/widgets/popup_menu.h"
#include "lottie/lottie_multi_player.h"
#include "lottie/lottie_animation.h"
#include "chat_helpers/stickers_lottie.h"
@@ -36,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "app.h"
#include "styles/style_layers.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_info.h"
#include <QtWidgets/QApplication>
#include <QtGui/QClipboard>
@@ -189,11 +192,10 @@ void StickerSetBox::addStickers() {
_inner->install();
}
void StickerSetBox::shareStickers() {
void StickerSetBox::copyStickersLink() {
const auto url = _controller->session().createInternalLinkFull(
qsl("addstickers/") + _inner->shortName());
QGuiApplication::clipboard()->setText(url);
Ui::show(Box<InformBox>(tr::lng_stickers_copied(tr::now)));
}
void StickerSetBox::updateTitleAndButtons() {
@@ -207,10 +209,33 @@ void StickerSetBox::updateButtons() {
if (_inner->notInstalled()) {
addButton(tr::lng_stickers_add_pack(), [=] { addStickers(); });
addButton(tr::lng_cancel(), [=] { closeBox(); });
if (!_inner->shortName().isEmpty()) {
const auto top = addTopButton(st::infoTopBarMenu);
const auto share = [=] {
copyStickersLink();
Ui::Toast::Show(tr::lng_stickers_copied(tr::now));
closeBox();
};
const auto menu =
std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
top->setClickedCallback([=] {
*menu = base::make_unique_q<Ui::PopupMenu>(top);
(*menu)->addAction(
tr::lng_stickers_share_pack(tr::now),
share);
(*menu)->popup(QCursor::pos());
return true;
});
}
} else if (_inner->official()) {
addButton(tr::lng_about_done(), [=] { closeBox(); });
} else {
addButton(tr::lng_stickers_share_pack(), [=] { shareStickers(); });
auto share = [=] {
copyStickersLink();
Ui::Toast::Show(tr::lng_stickers_copied(tr::now));
};
addButton(tr::lng_stickers_share_pack(), std::move(share));
addButton(tr::lng_cancel(), [=] { closeBox(); });
}
} else {

View File

@@ -41,7 +41,7 @@ private:
void updateTitleAndButtons();
void updateButtons();
void addStickers();
void shareStickers();
void copyStickersLink();
const not_null<Window::SessionController*> _controller;
MTPInputStickerSet _set;

View File

@@ -179,7 +179,7 @@ private:
return false;
}
};
using SelectedRow = base::optional_variant<MegagroupSet, int>;
using SelectedRow = std::variant<v::null_t, MegagroupSet, int>;
class AddressField : public Ui::UsernameInput {
public:
using UsernameInput::UsernameInput;
@@ -1101,7 +1101,7 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> row, int index) {
if (_megagroupSet) {
auto selectedIndex = [&] {
if (auto index = base::get_if<int>(&_selected)) {
if (auto index = std::get_if<int>(&_selected)) {
return *index;
}
return -1;
@@ -1341,7 +1341,7 @@ void StickersBox::Inner::mousePressEvent(QMouseEvent *e) {
if (_actionSel >= 0) {
setActionDown(_actionSel);
update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight);
} else if (auto selectedIndex = base::get_if<int>(&_selected)) {
} else if (auto selectedIndex = std::get_if<int>(&_selected)) {
if (_section == Section::Installed && !_rows[*selectedIndex]->isRecentSet() && _inDragArea) {
_above = _dragging = _started = *selectedIndex;
_dragStart = mapFromGlobal(_mouse);
@@ -1394,7 +1394,7 @@ void StickersBox::Inner::setSelected(SelectedRow selected) {
return;
}
auto countSelectedIndex = [&] {
if (auto index = base::get_if<int>(&_selected)) {
if (auto index = std::get_if<int>(&_selected)) {
return *index;
}
return -1;
@@ -1416,7 +1416,7 @@ void StickersBox::Inner::setPressed(SelectedRow pressed) {
return;
}
auto countPressedIndex = [&] {
if (auto index = base::get_if<int>(&_pressed)) {
if (auto index = std::get_if<int>(&_pressed)) {
return *index;
}
return -1;
@@ -1544,7 +1544,7 @@ void StickersBox::Inner::updateCursor() {
? ((_actionSel >= 0 && (_actionDown < 0 || _actionDown == _actionSel))
? style::cur_pointer
: style::cur_default)
: (_selected.has_value() || _pressed.has_value())
: (!v::is_null(_selected) || !v::is_null(_pressed))
? style::cur_pointer
: style::cur_default);
}
@@ -1582,7 +1582,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
_dragging = _started = -1;
} else if (pressed == _selected && _actionSel < 0 && _actionDown < 0) {
const auto selectedIndex = [&] {
if (auto index = base::get_if<int>(&_selected)) {
if (auto index = std::get_if<int>(&_selected)) {
return *index;
}
return -1;
@@ -1602,7 +1602,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
showSetByRow(*row);
}
}
} else if (_megagroupSelectedSet && _selected.is<MegagroupSet>()) {
} else if (_megagroupSelectedSet && v::is<MegagroupSet>(_selected)) {
showSetByRow(*_megagroupSelectedSet);
}
}

View File

@@ -262,8 +262,8 @@ callReDial: IconButton {
width: 40px;
height: 56px;
icon: mainMenuCalls;
iconOver: mainMenuCallsOver;
icon: icon {{ "call_answer", menuIconFg }};
iconOver: icon {{ "call_answer", menuIconFgOver }};
iconPosition: point(-1px, -1px);
ripple: defaultRippleAnimation;
@@ -271,6 +271,11 @@ callReDial: IconButton {
rippleAreaSize: 40px;
}
callCameraReDial: IconButton(callReDial) {
icon: icon {{ "call_camera_active", menuIconFg }};
iconOver: icon {{ "call_camera_active", menuIconFgOver }};
}
callRatingPadding: margins(24px, 12px, 24px, 0px);
callRatingStar: IconButton {
width: 36px;
@@ -390,3 +395,7 @@ callTitle: WindowTitle(defaultWindowTitle) {
closeIconActiveOver: callTitleCloseIconOver;
}
callTitleShadow: icon {{ "calls_shadow_controls", windowShadowFg }};
callErrorToast: Toast(defaultToast) {
minWidth: 240px;
}

View File

@@ -42,6 +42,11 @@ public:
Missed,
};
enum class CallType {
Voice,
Video,
};
bool canAddItem(not_null<const HistoryItem*> item) const {
return (ComputeType(item) == _type)
&& (ItemDateTime(item).date() == _date);
@@ -90,7 +95,7 @@ public:
return 0;
}
QSize actionSize() const override {
return peer()->isUser() ? QSize(st::callReDial.width, st::callReDial.height) : QSize();
return peer()->isUser() ? QSize(_st->width, _st->height) : QSize();
}
QMargins actionMargins() const override {
return QMargins(
@@ -110,10 +115,12 @@ public:
private:
void refreshStatus() override;
static Type ComputeType(not_null<const HistoryItem*> item);
static CallType ComputeCallType(not_null<const HistoryItem*> item);
std::vector<not_null<HistoryItem*>> _items;
QDate _date;
Type _type;
not_null<const style::IconButton*> _st;
std::unique_ptr<Ui::RippleAnimation> _actionRipple;
@@ -123,7 +130,10 @@ BoxController::Row::Row(not_null<HistoryItem*> item)
: PeerListRow(item->history()->peer, item->id)
, _items(1, item)
, _date(ItemDateTime(item).date())
, _type(ComputeType(item)) {
, _type(ComputeType(item))
, _st(ComputeCallType(item) == CallType::Voice
? &st::callReDial
: &st::callCameraReDial) {
refreshStatus();
}
@@ -153,12 +163,18 @@ void BoxController::Row::paintAction(
bool actionSelected) {
auto size = actionSize();
if (_actionRipple) {
_actionRipple->paint(p, x + st::callReDial.rippleAreaPosition.x(), y + st::callReDial.rippleAreaPosition.y(), outerWidth);
_actionRipple->paint(
p,
x + _st->rippleAreaPosition.x(),
y + _st->rippleAreaPosition.y(),
outerWidth);
if (_actionRipple->empty()) {
_actionRipple.reset();
}
}
st::callReDial.icon.paintInCenter(p, style::rtlrect(x, y, size.width(), size.height(), outerWidth));
_st->icon.paintInCenter(
p,
style::rtlrect(x, y, size.width(), size.height(), outerWidth));
}
void BoxController::Row::refreshStatus() {
@@ -201,12 +217,28 @@ BoxController::Row::Type BoxController::Row::ComputeType(
return Type::In;
}
BoxController::Row::CallType BoxController::Row::ComputeCallType(
not_null<const HistoryItem*> item) {
if (auto media = item->media()) {
if (const auto call = media->call()) {
if (call->video) {
return CallType::Video;
}
}
}
return CallType::Voice;
}
void BoxController::Row::addActionRipple(QPoint point, Fn<void()> updateCallback) {
if (!_actionRipple) {
auto mask = Ui::RippleAnimation::ellipseMask(QSize(st::callReDial.rippleAreaSize, st::callReDial.rippleAreaSize));
_actionRipple = std::make_unique<Ui::RippleAnimation>(st::callReDial.ripple, std::move(mask), std::move(updateCallback));
auto mask = Ui::RippleAnimation::ellipseMask(
QSize(_st->rippleAreaSize, _st->rippleAreaSize));
_actionRipple = std::make_unique<Ui::RippleAnimation>(
_st->ripple,
std::move(mask),
std::move(updateCallback));
}
_actionRipple->add(point - st::callReDial.rippleAreaPosition);
_actionRipple->add(point - _st->rippleAreaPosition);
}
void BoxController::Row::stopLastActionRipple() {
@@ -240,8 +272,11 @@ void BoxController::prepare() {
}, lifetime());
session().changes().messageUpdates(
Data::MessageUpdate::Flag::CallAdded
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
Data::MessageUpdate::Flag::NewAdded
) | rpl::filter([=](const Data::MessageUpdate &update) {
const auto media = update.item->media();
return (media != nullptr) && (media->call() != nullptr);
}) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
insertRow(update.item, InsertWay::Prepend);
}, lifetime());
@@ -262,6 +297,7 @@ void BoxController::loadMoreRows() {
MTP_inputPeerEmpty(),
MTP_string(),
MTP_inputUserEmpty(),
MTPint(), // top_msg_id
MTP_inputMessagesFilterPhoneCalls(MTP_flags(0)),
MTP_int(0),
MTP_int(0),

View File

@@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_info.h"
#include "calls/calls_panel.h"
#include "webrtc/webrtc_video_track.h"
#include "webrtc/webrtc_media_devices.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "facades.h"
@@ -345,19 +346,34 @@ void Call::setMuted(bool mute) {
}
void Call::setupOutgoingVideo() {
static const auto hasDevices = [] {
return !Webrtc::GetVideoInputList().empty();
};
const auto started = _videoOutgoing->state();
if (!hasDevices()) {
_videoOutgoing->setState(Webrtc::VideoState::Inactive);
}
_videoOutgoing->stateValue(
) | rpl::start_with_next([=](Webrtc::VideoState state) {
if (_state.current() != State::Established
if (state != Webrtc::VideoState::Inactive && !hasDevices()) {
_errors.fire({ ErrorType::NoCamera });
_videoOutgoing->setState(Webrtc::VideoState::Inactive);
} else if (_state.current() != State::Established
&& state != started
&& !_videoCapture) {
_errors.fire({ ErrorType::NotStartedCall });
_videoOutgoing->setState(started);
} else if (state != Webrtc::VideoState::Inactive
&& _instance
&& !_instance->supportsVideo()) {
_errors.fire({ ErrorType::NotVideoCall });
_videoOutgoing->setState(Webrtc::VideoState::Inactive);
} else if (state != Webrtc::VideoState::Inactive) {
// Paused not supported right now.
#ifndef DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION
Assert(state == Webrtc::VideoState::Active);
if (!_videoCapture) {
_videoCapture = tgcalls::VideoCaptureInterface::Create();
_videoCapture = _delegate->getVideoCapture();
_videoCapture->setOutput(_videoOutgoing->sink());
}
if (_instance) {
@@ -409,7 +425,9 @@ void Call::redial() {
}
QString Call::getDebugLog() const {
return QString::fromStdString(_instance->getDebugInfo());
return _instance
? QString::fromStdString(_instance->getDebugInfo())
: QString();
}
void Call::startWaitingTrack() {
@@ -704,6 +722,8 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
auto encryptionKeyValue = std::make_shared<std::array<uint8_t, 256>>();
memcpy(encryptionKeyValue->data(), _authKey.data(), 256);
const auto &settings = Core::App().settings();
const auto weak = base::make_weak(this);
tgcalls::Descriptor descriptor = {
.config = tgcalls::Config{
@@ -720,6 +740,12 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
.encryptionKey = tgcalls::EncryptionKey(
std::move(encryptionKeyValue),
(_type == Type::Outgoing)),
.mediaDevicesConfig = tgcalls::MediaDevicesConfig{
.audioInputId = settings.callInputDeviceId().toStdString(),
.audioOutputId = settings.callOutputDeviceId().toStdString(),
.inputVolume = 1.f,//settings.callInputVolume() / 100.f,
.outputVolume = 1.f,//settings.callOutputVolume() / 100.f,
},
.videoCapture = _videoCapture,
.stateUpdated = [=](tgcalls::State state) {
crl::on_main(weak, [=] {
@@ -804,14 +830,6 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
}
raw->setIncomingVideoOutput(_videoIncoming->sink());
const auto &settings = Core::App().settings();
raw->setAudioOutputDevice(
settings.callOutputDeviceID().toStdString());
raw->setAudioInputDevice(
settings.callInputDeviceID().toStdString());
raw->setOutputVolume(settings.callOutputVolume() / 100.0f);
raw->setInputVolume(settings.callInputVolume() / 100.0f);
raw->setAudioOutputDuckingEnabled(settings.callAudioDuckingEnabled());
}
@@ -833,7 +851,9 @@ void Call::handleControllerStateChange(tgcalls::State state) {
} break;
case tgcalls::State::Failed: {
auto error = QString::fromStdString(_instance->getLastError());
auto error = _instance
? QString::fromStdString(_instance->getLastError())
: QString();
LOG(("Call Info: State changed to Failed, error: %1.").arg(error));
handleControllerError(error);
} break;
@@ -941,16 +961,23 @@ void Call::setState(State state) {
}
}
void Call::setCurrentAudioDevice(bool input, std::string deviceID) {
void Call::setCurrentAudioDevice(bool input, const QString &deviceId) {
if (_instance) {
const auto id = deviceId.toStdString();
if (input) {
_instance->setAudioInputDevice(deviceID);
_instance->setAudioInputDevice(id);
} else {
_instance->setAudioOutputDevice(deviceID);
_instance->setAudioOutputDevice(id);
}
}
}
void Call::setCurrentVideoDevice(const QString &deviceId) {
if (_videoCapture) {
_videoCapture->switchToDevice(deviceId.toStdString());
}
}
void Call::setAudioVolume(bool input, float level) {
if (_instance) {
if (input) {
@@ -1055,7 +1082,8 @@ void Call::handleControllerError(const QString &error) {
void Call::destroyController() {
if (_instance) {
const auto state = _instance->stop();
_instance->stop([](tgcalls::FinalState) {
});
DEBUG_LOG(("Call Info: Destroying call controller.."));
_instance.reset();

View File

@@ -40,6 +40,19 @@ struct DhConfig {
bytes::vector p;
};
enum class ErrorType {
NoCamera,
NoMicrophone,
NotStartedCall,
NotVideoCall,
Unknown,
};
struct Error {
ErrorType type = ErrorType::Unknown;
QString details;
};
class Call : public base::has_weak_ptr {
public:
class Delegate {
@@ -55,7 +68,9 @@ public:
Ended,
};
virtual void playSound(Sound sound) = 0;
virtual void requestPermissionsOrFail(Fn<void()> result) = 0;
virtual void requestPermissionsOrFail(Fn<void()> onSuccess) = 0;
virtual auto getVideoCapture()
-> std::shared_ptr<tgcalls::VideoCaptureInterface> = 0;
virtual ~Delegate() = default;
@@ -105,6 +120,10 @@ public:
return _state.value();
}
[[nodiscard]] rpl::producer<Error> errors() const {
return _errors.events();
}
enum class RemoteAudioState {
Muted,
Active,
@@ -155,7 +174,8 @@ public:
QString getDebugLog() const;
void setCurrentAudioDevice(bool input, std::string deviceID);
void setCurrentAudioDevice(bool input, const QString &deviceId);
void setCurrentVideoDevice(const QString &deviceId);
void setAudioVolume(bool input, float level);
void setAudioDuckingEnabled(bool enabled);
@@ -213,6 +233,7 @@ private:
rpl::variable<State> _state = State::Starting;
rpl::variable<RemoteAudioState> _remoteAudioState = RemoteAudioState::Active;
rpl::variable<Webrtc::VideoState> _remoteVideoState;
rpl::event_stream<Error> _errors;
FinishType _finishAfterRequestingCall = FinishType::None;
bool _answerAfterDhConfigReceived = false;
rpl::variable<int> _signalBarCount = kSignalBarStarting;

View File

@@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "mtproto/mtproto_config.h"
#include "boxes/rate_call_box.h"
#include "tgcalls/VideoCaptureInterface.h"
#include "app.h"
namespace Calls {
@@ -344,4 +345,15 @@ void Instance::requestPermissionOrFail(Platform::PermissionType type, Fn<void()>
}
}
std::shared_ptr<tgcalls::VideoCaptureInterface> Instance::getVideoCapture() {
if (auto result = _videoCapture.lock()) {
return result;
}
auto result = std::shared_ptr<tgcalls::VideoCaptureInterface>(
tgcalls::VideoCaptureInterface::Create(
Core::App().settings().callVideoInputDeviceId().toStdString()));
_videoCapture = result;
return result;
}
} // namespace Calls

View File

@@ -43,6 +43,7 @@ public:
void showInfoPanel(not_null<Call*> call);
[[nodiscard]] Call *currentCall() const;
[[nodiscard]] rpl::producer<Call*> currentCallValue() const;
std::shared_ptr<tgcalls::VideoCaptureInterface> getVideoCapture() override;
[[nodiscard]] bool isQuitPrevent();
@@ -78,6 +79,7 @@ private:
crl::time _lastServerConfigUpdateTime = 0;
base::weak_ptr<Main::Session> _serverConfigRequestSession;
std::weak_ptr<tgcalls::VideoCaptureInterface> _videoCapture;
std::unique_ptr<Call> _currentCall;
rpl::event_stream<Call*> _currentCallChanges;

View File

@@ -24,9 +24,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/window.h"
#include "ui/effects/ripple_animation.h"
#include "ui/image/image.h"
#include "ui/text/format_values.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/platform/ui_platform_utility.h"
#include "ui/toast/toast.h"
#include "ui/empty_userpic.h"
#include "ui/emoji_config.h"
#include "core/application.h"
@@ -37,7 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/platform_specific.h"
#include "base/platform/base_platform_info.h"
#include "window/main_window.h"
#include "layout.h"
#include "app.h"
#include "webrtc/webrtc_video_track.h"
#include "styles/style_calls.h"
@@ -52,6 +53,123 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QWindow>
namespace Calls {
namespace {
#if defined Q_OS_MAC && !defined OS_MAC_OLD
#define USE_OPENGL_OVERLAY_WIDGET
#endif // Q_OS_MAC && !OS_MAC_OLD
#ifdef USE_OPENGL_OVERLAY_WIDGET
using IncomingParent = Ui::RpWidgetWrap<QOpenGLWidget>;
#else // USE_OPENGL_OVERLAY_WIDGET
using IncomingParent = Ui::RpWidget;
#endif // USE_OPENGL_OVERLAY_WIDGET
} // namespace
class Panel::Incoming final : public IncomingParent {
public:
Incoming(
not_null<QWidget*> parent,
not_null<Webrtc::VideoTrack*> track);
private:
void paintEvent(QPaintEvent *e) override;
void initBottomShadow();
void fillTopShadow(QPainter &p);
void fillBottomShadow(QPainter &p);
const not_null<Webrtc::VideoTrack*> _track;
QPixmap _bottomShadow;
};
Panel::Incoming::Incoming(
not_null<QWidget*> parent,
not_null<Webrtc::VideoTrack*> track)
: IncomingParent(parent)
, _track(track) {
initBottomShadow();
setAttribute(Qt::WA_OpaquePaintEvent);
setAttribute(Qt::WA_TransparentForMouseEvents);
}
void Panel::Incoming::paintEvent(QPaintEvent *e) {
QPainter p(this);
const auto frame = _track->frame(Webrtc::FrameRequest());
if (frame.isNull()) {
p.fillRect(e->rect(), Qt::black);
} else {
auto hq = PainterHighQualityEnabler(p);
p.drawImage(rect(), frame);
fillBottomShadow(p);
fillTopShadow(p);
}
_track->markFrameShown();
}
void Panel::Incoming::initBottomShadow() {
auto image = QImage(
QSize(1, st::callBottomShadowSize) * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied);
const auto colorFrom = uint32(0);
const auto colorTill = uint32(74);
const auto rows = image.height();
const auto step = (uint64(colorTill - colorFrom) << 32) / rows;
auto accumulated = uint64();
auto bytes = image.bits();
for (auto y = 0; y != rows; ++y) {
accumulated += step;
const auto color = (colorFrom + uint32(accumulated >> 32)) << 24;
for (auto x = 0; x != image.width(); ++x) {
*(reinterpret_cast<uint32*>(bytes) + x) = color;
}
bytes += image.bytesPerLine();
}
_bottomShadow = Images::PixmapFast(std::move(image));
}
void Panel::Incoming::fillTopShadow(QPainter &p) {
#ifdef Q_OS_WIN
const auto width = parentWidget()->width();
const auto position = QPoint(width - st::callTitleShadow.width(), 0);
const auto shadowArea = QRect(
position,
st::callTitleShadow.size());
const auto fill = shadowArea.intersected(geometry()).translated(-pos());
if (fill.isEmpty()) {
return;
}
p.save();
p.setClipRect(fill);
st::callTitleShadow.paint(p, position - pos(), width);
p.restore();
#endif // Q_OS_WIN
}
void Panel::Incoming::fillBottomShadow(QPainter &p) {
const auto shadowArea = QRect(
0,
parentWidget()->height() - st::callBottomShadowSize,
parentWidget()->width(),
st::callBottomShadowSize);
const auto fill = shadowArea.intersected(geometry()).translated(-pos());
if (fill.isEmpty()) {
return;
}
const auto factor = cIntRetinaFactor();
p.drawPixmap(
fill,
_bottomShadow,
QRect(
0,
factor * (fill.y() - shadowArea.translated(-pos()).y()),
factor,
factor * fill.height()));
}
class Panel::Button final : public Ui::RippleButton {
public:
@@ -299,7 +417,6 @@ Panel::Panel(not_null<Call*> call)
initWidget();
initControls();
initLayout();
initBottomShadow();
showAndActivate();
}
@@ -318,6 +435,8 @@ void Panel::replaceCall(not_null<Call*> call) {
}
void Panel::initWindow() {
_window->setAttribute(Qt::WA_OpaquePaintEvent);
_window->setAttribute(Qt::WA_NoSystemBackground);
_window->setWindowIcon(
QIcon(QPixmap::fromImage(Image::Empty()->original(), Qt::ColorOnly)));
_window->setTitle(u" "_q);
@@ -459,16 +578,42 @@ void Panel::setIncomingSize(QSize size) {
if (_incomingFrameSize == size) {
return;
}
widget()->update(incomingFrameGeometry());
_incomingFrameSize = size;
widget()->update(incomingFrameGeometry());
refreshIncomingGeometry();
showControls();
}
void Panel::refreshIncomingGeometry() {
Expects(_call != nullptr);
Expects(_incoming != nullptr);
if (_incomingFrameSize.isEmpty()) {
_incoming->hide();
return;
}
const auto to = widget()->size();
const auto small = _incomingFrameSize.scaled(to, Qt::KeepAspectRatio);
const auto big = _incomingFrameSize.scaled(
to,
Qt::KeepAspectRatioByExpanding);
// If we cut out no more than 0.33 of the original, let's use expanding.
const auto use = ((big.width() * 3 <= to.width() * 4)
&& (big.height() * 3 <= to.height() * 4))
? big
: small;
const auto pos = QPoint(
(to.width() - use.width()) / 2,
(to.height() - use.height()) / 2);
_incoming->setGeometry(QRect(pos, use));
_incoming->show();
}
void Panel::reinitWithCall(Call *call) {
_callLifetime.destroy();
_call = call;
if (!_call) {
_incoming = nullptr;
_outgoingVideoBubble = nullptr;
return;
}
@@ -495,6 +640,10 @@ void Panel::reinitWithCall(Call *call) {
_outgoingVideoBubble = std::make_unique<VideoBubble>(
widget(),
_call->videoOutgoing());
_incoming = std::make_unique<Incoming>(
widget(),
_call->videoIncoming());
_incoming->hide();
_call->mutedValue(
) | rpl::start_with_next([=](bool mute) {
@@ -521,15 +670,14 @@ void Panel::reinitWithCall(Call *call) {
_call->videoIncoming()->renderNextFrame(
) | rpl::start_with_next([=] {
setIncomingSize(_call->videoIncoming()->frame({}).size());
if (_incomingFrameSize.isEmpty()) {
if (_incoming->isHidden()) {
return;
}
const auto incoming = incomingFrameGeometry();
const auto outgoing = outgoingFrameGeometry();
_incoming->update();
if (incoming.intersects(outgoing)) {
widget()->update(incoming.united(outgoing));
} else {
widget()->update(incoming);
widget()->update(outgoing);
}
}, _callLifetime);
@@ -537,10 +685,9 @@ void Panel::reinitWithCall(Call *call) {
) | rpl::start_with_next([=] {
const auto incoming = incomingFrameGeometry();
const auto outgoing = outgoingFrameGeometry();
widget()->update(outgoing);
if (incoming.intersects(outgoing)) {
widget()->update(incoming.united(outgoing));
} else {
widget()->update(outgoing);
_incoming->update();
}
}, _callLifetime);
@@ -557,8 +704,34 @@ void Panel::reinitWithCall(Call *call) {
}
}, _callLifetime);
_call->errors(
) | rpl::start_with_next([=](Error error) {
const auto text = [=] {
switch (error.type) {
case ErrorType::NoCamera:
return tr::lng_call_error_no_camera(tr::now);
case ErrorType::NotVideoCall:
return tr::lng_call_error_camera_outdated(tr::now, lt_user, _user->name);
case ErrorType::NotStartedCall:
return tr::lng_call_error_camera_not_started(tr::now);
//case ErrorType::NoMicrophone:
// return tr::lng_call_error_no_camera(tr::now);
case ErrorType::Unknown:
return Lang::Hard::CallErrorIncompatible();
}
Unexpected("Error type in _call->errors().");
}();
Ui::Toast::Show(widget(), Ui::Toast::Config{
.text = { text },
.st = &st::callErrorToast,
.multiline = true,
});
}, _callLifetime);
_name->setText(_user->name);
updateStatusText(_call->state());
_incoming->lower();
}
void Panel::createRemoteAudioMute() {
@@ -615,66 +788,6 @@ void Panel::initLayout() {
#endif // Q_OS_WIN
}
void Panel::initBottomShadow() {
auto image = QImage(
QSize(1, st::callBottomShadowSize) * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied);
const auto colorFrom = uint32(0);
const auto colorTill = uint32(74);
const auto rows = image.height();
const auto step = (uint64(colorTill - colorFrom) << 32) / rows;
auto accumulated = uint64();
auto bytes = image.bits();
for (auto y = 0; y != rows; ++y) {
accumulated += step;
const auto color = (colorFrom + uint32(accumulated >> 32)) << 24;
for (auto x = 0; x != image.width(); ++x) {
*(reinterpret_cast<uint32*>(bytes) + x) = color;
}
bytes += image.bytesPerLine();
}
_bottomShadow = Images::PixmapFast(std::move(image));
}
void Panel::fillTopShadow(QPainter &p, QRect incoming) {
#ifdef Q_OS_WIN
const auto width = widget()->width();
const auto position = QPoint(width - st::callTitleShadow.width(), 0);
const auto shadowArea = QRect(
position,
st::callTitleShadow.size());
const auto fill = shadowArea.intersected(incoming);
if (fill.isEmpty()) {
return;
}
p.save();
p.setClipRect(fill);
st::callTitleShadow.paint(p, position, width);
p.restore();
#endif // Q_OS_WIN
}
void Panel::fillBottomShadow(QPainter &p, QRect incoming) {
const auto shadowArea = QRect(
0,
widget()->height() - st::callBottomShadowSize,
widget()->width(),
st::callBottomShadowSize);
const auto fill = shadowArea.intersected(incoming);
if (fill.isEmpty()) {
return;
}
const auto factor = cIntRetinaFactor();
p.drawPixmap(
fill,
_bottomShadow,
QRect(
0,
factor * (fill.y() - shadowArea.y()),
factor,
factor * fill.height()));
}
void Panel::showControls() {
Expects(_call != nullptr);
@@ -683,6 +796,7 @@ void Panel::showControls() {
_cancel->setVisible(_cancel->toggled());
const auto shown = !_incomingFrameSize.isEmpty();
_incoming->setVisible(shown);
_name->setVisible(!shown);
_status->setVisible(!shown);
_userpic->setVisible(!shown);
@@ -726,24 +840,9 @@ void Panel::toggleFullScreen(bool fullscreen) {
}
QRect Panel::incomingFrameGeometry() const {
if (!_call || _incomingFrameSize.isEmpty()) {
return QRect();
}
const auto to = widget()->size();
const auto small = _incomingFrameSize.scaled(to, Qt::KeepAspectRatio);
const auto big = _incomingFrameSize.scaled(
to,
Qt::KeepAspectRatioByExpanding);
// If we cut out no more than 0.33 of the original, let's use expanding.
const auto use = ((big.width() * 3 <= to.width() * 4)
&& (big.height() * 3 <= to.height() * 4))
? big
: small;
const auto pos = QPoint(
(to.width() - use.width()) / 2,
(to.height() - use.height()) / 2);
return QRect(pos, use);
return (!_incoming || _incoming->isHidden())
? QRect()
: _incoming->geometry();
}
QRect Panel::outgoingFrameGeometry() const {
@@ -754,6 +853,9 @@ void Panel::updateControlsGeometry() {
if (widget()->size().isEmpty()) {
return;
}
if (_incoming) {
refreshIncomingGeometry();
}
if (_fingerprint) {
#ifdef Q_OS_WIN
const auto minRight = _controls->geometry().width()
@@ -875,21 +977,16 @@ void Panel::updateStatusGeometry() {
void Panel::paint(QRect clip) {
Painter p(widget());
p.fillRect(clip, st::callBgOpaque);
const auto incoming = incomingFrameGeometry();
if (!incoming.isEmpty()) {
Assert(_call != nullptr);
const auto frame = _call->videoIncoming()->frame(
Webrtc::FrameRequest());
if (!frame.isNull()) {
auto hq = PainterHighQualityEnabler(p);
p.drawImage(incoming, frame);
}
fillBottomShadow(p, incoming);
fillTopShadow(p, incoming);
auto region = QRegion(clip);
if (!_incoming->isHidden()) {
region = region.subtracted(QRegion(_incoming->geometry()));
}
for (const auto rect : region.rects()) {
p.fillRect(rect, st::callBgOpaque);
}
if (_incoming && _incoming->isHidden()) {
_call->videoIncoming()->markFrameShown();
}
_call->videoIncoming()->markFrameShown();
}
void Panel::handleClose() {
@@ -973,7 +1070,7 @@ void Panel::updateStatusText(State state) {
auto durationMs = _call->getDurationMs();
auto durationSeconds = durationMs / 1000;
startDurationUpdateTimer(durationMs);
return formatDurationText(durationSeconds);
return Ui::FormatDurationText(durationSeconds);
}
return tr::lng_call_status_ended(tr::now);
} break;

View File

@@ -55,7 +55,7 @@ public:
void closeBeforeDestroy();
private:
class Content;
class Incoming;
class Button;
using State = Call::State;
using Type = Call::Type;
@@ -75,7 +75,6 @@ private:
void reinitWithCall(Call *call);
void initLayout();
void initGeometry();
void initBottomShadow();
void handleClose();
@@ -91,8 +90,7 @@ private:
void updateStatusText(State state);
void startDurationUpdateTimer(crl::time currentDuration);
void setIncomingSize(QSize size);
void fillTopShadow(QPainter &p, QRect incoming);
void fillBottomShadow(QPainter &p, QRect incoming);
void refreshIncomingGeometry();
void refreshOutgoingPreviewInBody(State state);
void toggleFullScreen(bool fullscreen);
@@ -106,6 +104,7 @@ private:
not_null<UserData*> _user;
const std::unique_ptr<Ui::Window> _window;
std::unique_ptr<Incoming> _incoming;
#ifdef Q_OS_WIN
std::unique_ptr<Ui::Platform::TitleControls> _controls;

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/text/format_values.h"
#include "lang/lang_keys.h"
#include "core/application.h"
#include "calls/calls_call.h"
@@ -20,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "boxes/abstract_box.h"
#include "base/timer.h"
#include "layout.h"
#include "app.h"
#include "styles/style_calls.h"
#include "styles/style_layers.h"
@@ -162,7 +162,7 @@ void TopBar::updateDurationText() {
auto durationMs = _call->getDurationMs();
auto durationSeconds = durationMs / 1000;
startDurationUpdateTimer(durationMs);
_durationLabel->setText(formatDurationText(durationSeconds));
_durationLabel->setText(Ui::FormatDurationText(durationSeconds));
if (_durationLabel->width() != wasWidth) {
updateControlsGeometry();
}

View File

@@ -569,7 +569,7 @@ std::vector<QString> EmojiKeywords::languages() {
const auto yieldList = [&](const QStringList &list) {
result.insert(end(result), list.begin(), list.end());
};
yield(Lang::Current().id());
yield(Lang::Id());
yield(Lang::DefaultLanguageId());
yield(Lang::CurrentCloudManager().suggestedLanguage());
yield(Platform::SystemLanguage());

View File

@@ -263,8 +263,8 @@ void Row::paintRadio(Painter &p) {
const auto loading = _loading
? _loading->computeState()
: Ui::RadialState{ 0., 0, FullArcLength };
const auto isToggledSet = _state.current().is<Active>();
const auto isActiveSet = isToggledSet || _state.current().is<Loading>();
const auto isToggledSet = v::is<Active>(_state.current());
const auto isActiveSet = isToggledSet || v::is<Loading>(_state.current());
const auto toggled = _toggled.value(isToggledSet ? 1. : 0.);
const auto active = _active.value(isActiveSet ? 1. : 0.);
const auto _st = &st::defaultRadio;
@@ -345,7 +345,7 @@ void Row::onStateChanged(State was, StateChangeSource source) {
}
void Row::updateStatusColorOverride() {
const auto isToggledSet = _state.current().is<Active>();
const auto isToggledSet = v::is<Active>(_state.current());
const auto toggled = _toggled.value(isToggledSet ? 1. : 0.);
const auto over = showOver();
if (toggled == 0. && !over) {
@@ -373,7 +373,8 @@ void Row::setupContent(const Set &set) {
});
}) | rpl::flatten_latest(
) | rpl::filter([=](const SetState &state) {
return !_state.current().is<Failed>() || !state.is<Available>();
return !v::is<Failed>(_state.current())
|| !v::is<Available>(state);
});
setupLabels(set);
@@ -390,9 +391,10 @@ void Row::setupHandler() {
clicks(
) | rpl::filter([=] {
const auto &state = _state.current();
return !_switching && (state.is<Ready>() || state.is<Available>());
return !_switching && (v::is<Ready>(state)
|| v::is<Available>(state));
}) | rpl::start_with_next([=] {
if (_state.current().is<Available>()) {
if (v::is<Available>(_state.current())) {
load();
return;
}
@@ -409,7 +411,7 @@ void Row::setupHandler() {
_state.value(
) | rpl::map([=](const SetState &state) {
return state.is<Ready>() || state.is<Available>();
return v::is<Ready>(state) || v::is<Available>(state);
}) | rpl::start_with_next([=](bool active) {
setDisabled(!active);
setPointerCursor(active);
@@ -463,7 +465,7 @@ void Row::setupPreview(const Set &set) {
void Row::updateLoadingToFinished() {
_loading->update(
_state.current().is<Failed>() ? 0. : 1.,
v::is<Failed>(_state.current()) ? 0. : 1.,
true,
crl::now());
}
@@ -471,7 +473,7 @@ void Row::updateLoadingToFinished() {
void Row::radialAnimationCallback(crl::time now) {
const auto updated = [&] {
const auto state = _state.current();
if (const auto loading = base::get_if<Loading>(&state)) {
if (const auto loading = std::get_if<Loading>(&state)) {
return _loading->update(CountProgress(loading), false, now);
} else {
updateLoadingToFinished();
@@ -493,7 +495,7 @@ void Row::setupAnimation() {
_state.value(
) | rpl::map(
_1 == Active()
_1 == SetState{ Active() }
) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](bool toggled) {
_toggled.start(
@@ -505,7 +507,7 @@ void Row::setupAnimation() {
_state.value(
) | rpl::map([](const SetState &state) {
return state.is<Loading>() || state.is<Active>();
return v::is<Loading>(state) || v::is<Active>(state);
}) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](bool active) {
_active.start(
@@ -517,7 +519,7 @@ void Row::setupAnimation() {
_state.value(
) | rpl::map([](const SetState &state) {
return base::get_if<Loading>(&state);
return std::get_if<Loading>(&state);
}) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](const Loading *loading) {
if (loading && !_loading) {

View File

@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "chat_helpers/gifs_list_widget.h"
#include "apiwrap.h" // ApiWrap::toggleSavedGif
#include "api/api_toggling_media.h" // Api::ToggleSavedGif
#include "base/const_string.h"
#include "data/data_photo.h"
#include "data/data_document.h"
@@ -48,7 +48,7 @@ constexpr auto kSearchBotUsername = "gif"_cs;
void DeleteSavedGif(not_null<DocumentData*> document) {
auto &data = document->owner();
document->session().api().toggleSavedGif(
Api::ToggleSavedGif(
document,
Data::FileOriginSavedGifs(),
false);

View File

@@ -150,13 +150,26 @@ void EditLinkBox::prepare() {
session);
InitSpellchecker(_controller, text);
const auto url = content->add(
object_ptr<Ui::InputField>(
const auto placeholder = content->add(
object_ptr<Ui::RpWidget>(content),
st::markdownLinkFieldPadding);
placeholder->setAttribute(Qt::WA_TransparentForMouseEvents);
const auto url = Ui::AttachParentChild(
content,
object_ptr<Ui::MaskedInputField>(
content,
st::defaultInputField,
tr::lng_formatting_link_url(),
_startLink.trimmed()),
st::markdownLinkFieldPadding);
_startLink.trimmed()));
url->heightValue(
) | rpl::start_with_next([placeholder](int height) {
placeholder->resize(placeholder->width(), height);
}, placeholder->lifetime());
placeholder->widthValue(
) | rpl::start_with_next([=](int width) {
url->resize(width, url->height());
}, placeholder->lifetime());
url->move(placeholder->pos());
const auto submit = [=] {
const auto linkText = text->getLastText();
@@ -178,7 +191,7 @@ void EditLinkBox::prepare() {
connect(text, &Ui::InputField::submitted, [=] {
url->setFocusFast();
});
connect(url, &Ui::InputField::submitted, [=] {
connect(url, &Ui::MaskedInputField::submitted, [=] {
if (text->getLastText().isEmpty()) {
text->setFocusFast();
} else {
@@ -198,7 +211,11 @@ void EditLinkBox::prepare() {
setDimensions(st::boxWidth, content->height());
_setInnerFocus = [=] {
(_startText.isEmpty() ? text : url)->setFocusFast();
if (_startText.isEmpty()) {
text->setFocusFast();
} else {
url->setFocusFast();
}
};
}

View File

@@ -366,7 +366,7 @@ std::vector<int> DefaultLanguages() {
append(method->locale());
}
append(QLocale(Platform::SystemLanguage()));
append(QLocale(Lang::LanguageIdOrDefault(Lang::Current().id())));
append(QLocale(Lang::LanguageIdOrDefault(Lang::Id())));
return langs;
}

View File

@@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "apiwrap.h"
#include "api/api_toggling_media.h" // Api::ToggleFavedSticker
#include "app.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_window.h"
@@ -142,7 +143,7 @@ private:
Search,
Settings,
};
using OverState = base::variant<SpecialOver, int>;
using OverState = std::variant<SpecialOver, int>;
template <typename Callback>
void enumerateVisibleIcons(Callback callback);
@@ -547,7 +548,7 @@ void StickersListWidget::Footer::mouseMoveEvent(QMouseEvent *e) {
if (!_iconsDragging
&& !_icons.empty()
&& base::get_if<int>(&_iconDown) != nullptr) {
&& v::is<int>(_iconDown)) {
if ((_iconsMousePos - _iconsMouseDown).manhattanLength() >= QApplication::startDragDistance()) {
_iconsDragging = true;
}
@@ -578,7 +579,7 @@ void StickersListWidget::Footer::mouseReleaseEvent(QMouseEvent *e) {
updateSelected();
if (wasDown == _iconOver) {
if (const auto index = base::get_if<int>(&_iconOver)) {
if (const auto index = std::get_if<int>(&_iconOver)) {
_iconSelX = anim::value(
*index * st::stickerIconWidth,
*index * st::stickerIconWidth);
@@ -603,7 +604,7 @@ bool StickersListWidget::Footer::eventHook(QEvent *e) {
if (e->type() == QEvent::TouchBegin) {
} else if (e->type() == QEvent::Wheel) {
if (!_icons.empty()
&& (base::get_if<int>(&_iconOver) != nullptr)
&& v::is<int>(_iconOver)
&& (_iconDown == SpecialOver::None)) {
scrollByWheelEvent(static_cast<QWheelEvent*>(e));
}
@@ -634,7 +635,7 @@ void StickersListWidget::Footer::scrollByWheelEvent(
}
void StickersListWidget::Footer::updateSelected() {
if (_iconDown >= 0) {
if (_iconDown != SpecialOver::None) {
return;
}
@@ -1508,8 +1509,10 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
}
auto &sets = shownSets();
auto selectedSticker = base::get_if<OverSticker>(&_selected);
auto selectedButton = base::get_if<OverButton>(_pressed ? &_pressed : &_selected);
auto selectedSticker = std::get_if<OverSticker>(&_selected);
auto selectedButton = std::get_if<OverButton>(!v::is_null(_pressed)
? &_pressed
: &_selected);
if (sets.empty() && _section == Section::Search) {
paintEmptySearchResults(p);
@@ -1624,7 +1627,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
if (clip.top() + clip.height() <= info.rowsTop) {
return true;
} else if (set.id == Data::Stickers::MegagroupSetId && set.stickers.empty()) {
auto buttonSelected = (base::get_if<OverGroupAdd>(&_selected) != nullptr);
auto buttonSelected = (std::get_if<OverGroupAdd>(&_selected) != nullptr);
paintMegagroupEmptySet(p, info.rowsTop, buttonSelected);
return true;
}
@@ -1970,20 +1973,20 @@ void StickersListWidget::mousePressEvent(QMouseEvent *e) {
}
void StickersListWidget::setPressed(OverState newPressed) {
if (auto button = base::get_if<OverButton>(&_pressed)) {
if (auto button = std::get_if<OverButton>(&_pressed)) {
auto &sets = shownSets();
Assert(button->section >= 0 && button->section < sets.size());
auto &set = sets[button->section];
if (set.ripple) {
set.ripple->lastStop();
}
} else if (base::get_if<OverGroupAdd>(&_pressed)) {
} else if (std::get_if<OverGroupAdd>(&_pressed)) {
if (_megagroupSetButtonRipple) {
_megagroupSetButtonRipple->lastStop();
}
}
_pressed = newPressed;
if (auto button = base::get_if<OverButton>(&_pressed)) {
if (auto button = std::get_if<OverButton>(&_pressed)) {
auto &sets = shownSets();
Assert(button->section >= 0 && button->section < sets.size());
auto &set = sets[button->section];
@@ -1991,7 +1994,7 @@ void StickersListWidget::setPressed(OverState newPressed) {
set.ripple = createButtonRipple(button->section);
}
set.ripple->add(mapFromGlobal(QCursor::pos()) - buttonRippleTopLeft(button->section));
} else if (base::get_if<OverGroupAdd>(&_pressed)) {
} else if (std::get_if<OverGroupAdd>(&_pressed)) {
if (!_megagroupSetButtonRipple) {
auto maskSize = _megagroupSetButtonRect.size();
auto mask = Ui::RippleAnimation::roundRectMask(maskSize, st::buttonRadius);
@@ -2058,10 +2061,10 @@ void StickersListWidget::fillContextMenu(
SendMenu::Type type) {
auto selected = _selected;
auto &sets = shownSets();
if (!selected || _pressed) {
if (v::is_null(selected) || !v::is_null(_pressed)) {
return;
}
if (auto sticker = base::get_if<OverSticker>(&selected)) {
if (auto sticker = std::get_if<OverSticker>(&selected)) {
Assert(sticker->section >= 0 && sticker->section < sets.size());
auto &set = sets[sticker->section];
Assert(sticker->index >= 0 && sticker->index < set.stickers.size());
@@ -2079,10 +2082,9 @@ void StickersListWidget::fillContextMenu(
SendMenu::DefaultScheduleCallback(this, type, send));
const auto toggleFavedSticker = [=] {
document->session().api().toggleFavedSticker(
Api::ToggleFavedSticker(
document,
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0),
!document->owner().stickers().isFaved(document));
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0));
};
menu->addAction(
(document->owner().stickers().isFaved(document)
@@ -2093,6 +2095,15 @@ void StickersListWidget::fillContextMenu(
menu->addAction(tr::lng_context_pack_info(tr::now), [=] {
showStickerSetBox(document);
});
if (const auto id = set.id; id == Data::Stickers::RecentSetId) {
menu->addAction(tr::lng_recent_stickers_remove(tr::now), [=] {
Api::ToggleRecentSticker(
document,
Data::FileOriginStickerSet(id, 0),
false);
});
}
}
}
@@ -2100,7 +2111,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
_previewTimer.cancel();
auto pressed = _pressed;
setPressed(std::nullopt);
setPressed(v::null);
if (pressed != _selected) {
update();
}
@@ -2115,8 +2126,8 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
updateSelected();
auto &sets = shownSets();
if (pressed && pressed == _selected) {
if (auto sticker = base::get_if<OverSticker>(&pressed)) {
if (!v::is_null(pressed) && pressed == _selected) {
if (auto sticker = std::get_if<OverSticker>(&pressed)) {
Assert(sticker->section >= 0 && sticker->section < sets.size());
auto &set = sets[sticker->section];
Assert(sticker->index >= 0 && sticker->index < set.stickers.size());
@@ -2136,10 +2147,10 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
} else {
_chosen.fire_copy({ .document = document });
}
} else if (auto set = base::get_if<OverSet>(&pressed)) {
} else if (auto set = std::get_if<OverSet>(&pressed)) {
Assert(set->section >= 0 && set->section < sets.size());
displaySet(sets[set->section].id);
} else if (auto button = base::get_if<OverButton>(&pressed)) {
} else if (auto button = std::get_if<OverButton>(&pressed)) {
Assert(button->section >= 0 && button->section < sets.size());
if (sets[button->section].externalLayout) {
installSet(sets[button->section].id);
@@ -2150,7 +2161,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
} else {
removeSet(sets[button->section].id);
}
} else if (base::get_if<OverGroupAdd>(&pressed)) {
} else if (std::get_if<OverGroupAdd>(&pressed)) {
Ui::show(Box<StickersBox>(controller(), _megagroupSet));
}
}
@@ -2219,7 +2230,7 @@ void StickersListWidget::removeFavedSticker(int section, int index) {
const auto &sticker = _mySets[section].stickers[index];
const auto document = sticker.document;
session().data().stickers().setFaved(document, false);
session().api().toggleFavedSticker(
Api::ToggleFavedSticker(
document,
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0),
false);
@@ -2262,8 +2273,8 @@ void StickersListWidget::enterFromChildEvent(QEvent *e, QWidget *child) {
}
void StickersListWidget::clearSelection() {
setPressed(std::nullopt);
setSelected(std::nullopt);
setPressed(v::null);
setSelected(v::null);
update();
}
@@ -2741,11 +2752,11 @@ bool StickersListWidget::preventAutoHide() {
}
void StickersListWidget::updateSelected() {
if (_pressed && !_previewShown) {
if (!v::is_null(_pressed) && !_previewShown) {
return;
}
auto newSelected = OverState { std::nullopt };
auto newSelected = OverState { v::null };
auto p = mapFromGlobal(_lastMousePosition);
if (!rect().contains(p)
|| p.y() < getVisibleTop() || p.y() >= getVisibleBottom()
@@ -2818,13 +2829,15 @@ bool StickersListWidget::stickerHasDeleteButton(const Set &set, int index) const
void StickersListWidget::setSelected(OverState newSelected) {
if (_selected != newSelected) {
setCursor(newSelected ? style::cur_pointer : style::cur_default);
setCursor(!v::is_null(newSelected)
? style::cur_pointer
: style::cur_default);
auto &sets = shownSets();
auto updateSelected = [&]() {
if (auto sticker = base::get_if<OverSticker>(&_selected)) {
if (auto sticker = std::get_if<OverSticker>(&_selected)) {
rtlupdate(stickerRect(sticker->section, sticker->index));
} else if (auto button = base::get_if<OverButton>(&_selected)) {
} else if (auto button = std::get_if<OverButton>(&_selected)) {
if (button->section >= 0
&& button->section < sets.size()
&& sets[button->section].externalLayout) {
@@ -2832,7 +2845,7 @@ void StickersListWidget::setSelected(OverState newSelected) {
} else {
rtlupdate(removeButtonRect(button->section));
}
} else if (base::get_if<OverGroupAdd>(&_selected)) {
} else if (std::get_if<OverGroupAdd>(&_selected)) {
rtlupdate(megagroupSetButtonRectFinal());
}
};
@@ -2841,7 +2854,7 @@ void StickersListWidget::setSelected(OverState newSelected) {
updateSelected();
if (_previewShown && _pressed != _selected) {
if (const auto sticker = base::get_if<OverSticker>(&_selected)) {
if (const auto sticker = std::get_if<OverSticker>(&_selected)) {
_pressed = _selected;
Assert(sticker->section >= 0 && sticker->section < sets.size());
const auto &set = sets[sticker->section];
@@ -2856,7 +2869,7 @@ void StickersListWidget::setSelected(OverState newSelected) {
}
void StickersListWidget::showPreview() {
if (const auto sticker = base::get_if<OverSticker>(&_pressed)) {
if (const auto sticker = std::get_if<OverSticker>(&_pressed)) {
const auto &sets = shownSets();
Assert(sticker->section >= 0 && sticker->section < sets.size());
const auto &set = sets[sticker->section];

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