Compare commits

...

232 Commits

Author SHA1 Message Date
John Preston
fd633ecb49 Version 5.0.
- Choose custom font family in Settings > Chat settings > Font family.
- Show "Frequent contacts" when you focus the search field.
- Show "Recent chats" when you focus the search field.
- Show "Channels" list and similar channels.
- Premium users can use animated emoji in polls.
- Group admins can mass-moderate many messages.
- Fix frequent crashes on some Linux systems.
2024-05-02 14:11:20 +04:00
John Preston
9c77346f36 Fix build with Xcode and GCC. 2024-05-02 14:11:20 +04:00
John Preston
641e74763f Revert non-production-ready prepare.py changes.
Accidentally commited in b9b7d9e337.
2024-05-02 12:17:57 +04:00
John Preston
2e71427427 Improve font size edge cases. 2024-05-02 12:05:46 +04:00
John Preston
95ec0633ed Use custom-adjusted font metrics for custom fonts.
Fixes #27814.
2024-05-02 11:33:47 +04:00
23rd
14e8b8fb91 Fixed spam reporting in moderation box. 2024-05-02 04:09:25 +03:00
23rd
d40951f068 Added ability to moderate non-users to moderation box. 2024-05-02 04:09:25 +03:00
23rd
ab85d18cc8 Added support of personal channel with message. 2024-05-02 04:09:25 +03:00
23rd
6becaaa953 Slightly improved view style of giveaway results in dialogs list. 2024-05-02 04:09:25 +03:00
23rd
26e8c29f40 Moved formatting of dialog last date to td_ui. 2024-05-02 04:09:25 +03:00
John Preston
47800ee02d Improve font size selection a bit. 2024-05-01 19:09:54 +04:00
John Preston
12a24dd473 Fix possible crash in common groups list.
Currently Data::Session::processChat() may change adminRights(),
that may change Data::CanSendAnyOf(peer, ...), that may lead to
Window::SessionController::updateThirdColumnToCurrentChat() call,
that destroys current third column to replace it with another one.

If common groups list was opened in the third column this will crash.

Fixes #27640.
2024-05-01 17:52:40 +04:00
23rd
ad6321d3ae Fixed possible crash in moderate box. 2024-05-01 14:10:37 +03:00
John Preston
7e071c770f Fix possible crash with custom wallpapered peers. 2024-05-01 14:38:06 +04:00
John Preston
a62d1dfa63 Focus chats search by Ctrl[Cmd]+F. 2024-05-01 14:38:05 +04:00
John Preston
b3eb1dbc14 Revert "Toggle search focus by escape."
This reverts commit 72d5a9b3e0.
2024-05-01 14:38:05 +04:00
Ilya Fedin
3170a45158 Increase snapcraft verbosity 2024-05-01 14:06:23 +04:00
John Preston
32483fa13b Just focus the search field on Space. 2024-05-01 12:57:47 +04:00
John Preston
9166a1c3a6 Update Linux Qt patches. Hope fixes crashes. 2024-05-01 12:57:07 +04:00
GitHub Action
6fa0afff37 Update User-Agent for DNS to Chrome 124.0.0.0. 2024-05-01 12:47:51 +04:00
Ilya Fedin
bc649af941 Schedule no-response action runs at different times
To avoid rate limits
2024-05-01 12:47:42 +04:00
John Preston
16ce5ef046 Greeting category first in ChatIntro setup. 2024-05-01 12:47:20 +04:00
John Preston
bb6fd4bc4d Update scheme, new sticker categories. 2024-05-01 12:47:20 +04:00
John Preston
63e1731d7c Fix premium preview bullet-dots.
Regression was introduced in 88751896af.
2024-05-01 12:47:20 +04:00
John Preston
b9b7d9e337 Implement live location view. 2024-05-01 12:47:20 +04:00
23rd
ef8c07e6eb Added ability to translate selected text in profile section. 2024-04-30 17:35:34 +03:00
23rd
20a13663a6 Slightly improved style of rpl text mapping. 2024-04-30 17:35:04 +03:00
23rd
2f0aa6ef05 Removed state sending of low level of battery to call participant. 2024-04-30 16:49:17 +03:00
23rd
62bd1354dc Replaced box for leaving or deleting chat with generic box. 2024-04-28 20:14:26 +03:00
23rd
be255f1d09 Added to moderate box number of all messages that will be deleted. 2024-04-28 20:14:26 +03:00
23rd
b8b02b2285 Replaced list of history items for sending now with sorted by date. 2024-04-28 13:33:04 +03:00
23rd
e0db0642bd Fixed crash when deleting message from non-user. 2024-04-27 16:39:13 +03:00
23rd
e81465a54e Added Enter shortcut to moderation box for confirmation. 2024-04-27 13:31:00 +03:00
23rd
6e67cfc7be Fixed possible crash in moderation box. 2024-04-27 12:59:36 +03:00
John Preston
c0db5ee98a Beta version 4.16.10: Fix GCC build. 2024-04-26 23:41:28 +04:00
John Preston
372b3da09c Beta version 4.16.10.
- Group admins can mass-moderate many messages.
- Premium users can use animated emoji in polls.
- Revert the default "Open Sans" font to 1.10.
- Several crash fixes and small improvements.
2024-04-26 20:55:06 +04:00
John Preston
79532954dc Allow a bit more font size adjusting. 2024-04-26 20:18:30 +04:00
23rd
aff2be605e Removed item for poll creation from menu when it is impossible. 2024-04-26 19:15:03 +03:00
John Preston
363c191a6e Skip media bottom skip in IV. 2024-04-26 20:12:30 +04:00
John Preston
2949cdab61 Don't request IV two times in a row. 2024-04-26 20:12:29 +04:00
John Preston
7addcf2d25 Add IV footer. 2024-04-26 20:12:29 +04:00
John Preston
a272807a99 Remove "Create poll" button in Replies chat.
Fixes #27817.
2024-04-26 20:12:29 +04:00
23rd
c803603de4 Added ability to insert custom emoji to polls. 2024-04-26 20:12:29 +04:00
23rd
e6c22ec1ca Added api support for custom emoji in polls. 2024-04-26 20:12:29 +04:00
John Preston
b3ae843f0e Update API scheme to layer 179. 2024-04-26 20:12:29 +04:00
John Preston
12a78c1f45 Allow pasting text to the unfocused search. 2024-04-26 20:12:29 +04:00
23rd
612b81ee87 Fixed allowed reactions for channel posts in linked chats. 2024-04-26 20:12:29 +04:00
23rd
e5b91d2f3d Added entry points for moderation box. 2024-04-26 20:12:29 +04:00
23rd
82293c98eb Added arrow icon to divider link in moderation box. 2024-04-26 20:12:29 +04:00
23rd
629da68cfc Added api support to moderation box. 2024-04-26 20:12:29 +04:00
23rd
643ecd2c2c Implemented initial ui of moderation box. 2024-04-26 20:12:29 +04:00
Ilya Fedin
dd1cb00c62 Update snap to core24 2024-04-26 18:39:56 +04:00
23rd
2c4d8418c1 Removed unused code for sponsored messages. 2024-04-26 11:21:41 +04:00
23rd
9fcb5d6f31 Added emoji button to input field for peer title edit. 2024-04-26 11:21:41 +04:00
23rd
38fc6bfbb9 Added special mode to emoji list widget to exclude non-unicode emoji. 2024-04-26 11:21:41 +04:00
23rd
804991a69c Added universal duration to styles. 2024-04-26 11:21:41 +04:00
23rd
7388f46adf Fixed display of subscribers count in dialogs suggestions. 2024-04-26 11:21:41 +04:00
John Preston
10c427127e Fix accidental search focusing. 2024-04-26 11:10:12 +04:00
John Preston
c6d034174b Support separate webview storages. 2024-04-26 08:58:42 +04:00
Ilya Fedin
471831bcd6 Revert "Remove not really needed notification capability checks"
This reverts commit abdfa4f785.
2024-04-25 15:07:51 +04:00
Ilya Fedin
b1e64419a5 Fix setting application icon in Linux notifications 2024-04-25 11:31:43 +04:00
Ilya Fedin
e2f17f1131 Remove unneeded includes from notifications_manager_linux 2024-04-25 11:31:43 +04:00
John Preston
ae4a73a15b Rollback OpenSans to 1.10. 2024-04-24 22:20:22 +04:00
Kolya
a84ca00270 fix typo 2024-04-24 19:18:33 +04:00
John Preston
c16d820b88 Move OpenSSL probing before crash reporter. 2024-04-24 19:12:46 +04:00
John Preston
ef614150d5 Remove dead code in lib_ui. 2024-04-24 17:44:51 +04:00
John Preston
b46ca1ec17 Use different font fallback algo on Windows. 2024-04-24 17:44:40 +04:00
John Preston
d5a347ede7 Try cancelling the new search by mouse back button. 2024-04-24 17:41:12 +04:00
John Preston
72d5a9b3e0 Toggle search focus by escape. 2024-04-24 16:56:20 +04:00
John Preston
e6d72b4861 Un-focus chats search on empty history click. 2024-04-24 16:54:10 +04:00
John Preston
3da51b1bc9 Fix crash on quit in sponsored messages. 2024-04-24 16:37:27 +04:00
John Preston
17b7db6219 Request 64 frequent contacts instead of 32. 2024-04-24 16:37:27 +04:00
John Preston
8353180161 Fix phrase cut-off in QR login. 2024-04-24 16:37:27 +04:00
John Preston
f675a8dcf7 Attempt to fix a couple of crashes. 2024-04-24 16:37:27 +04:00
John Preston
493f1d69e2 Fix cancel search button overlap. 2024-04-24 16:37:27 +04:00
John Preston
331e8c3ec6 Beta version 4.16.9.
- Show "Frequent contacts" when you focus the search field.
- Show "Recent chats" when you focus the search field.
- Show "Channels" and channel recommendations.
- Allow changing font in Settings > Chat settings > Font family.
2024-04-23 23:14:19 +04:00
John Preston
56bce70558 Fix "long time ago" for users who blocked me. 2024-04-23 21:47:57 +04:00
John Preston
d82e48f8e4 Improve ChooseFontBox navigation. 2024-04-23 19:17:42 +04:00
John Preston
97ecc57be8 Add choose font settings. 2024-04-23 19:17:42 +04:00
John Preston
25bd2b145b Update submodules. 2024-04-23 19:17:42 +04:00
John Preston
7111c92ae7 Fix keyboard navigation in top peers. 2024-04-23 19:17:42 +04:00
John Preston
e066cf1589 Add "Show all" for top peers. 2024-04-23 19:17:41 +04:00
John Preston
6f328b2ef8 Fix inline query draft applying. 2024-04-23 19:17:41 +04:00
23rd
7a6c55bd8a Fixed color of icon for replies with media story. 2024-04-23 19:17:41 +04:00
23rd
b35d3f57fe Slightly improved style of sub-button in sponsored messages. 2024-04-23 19:17:41 +04:00
23rd
eef65af173 Slightly simplified meson usage on macOS in prepare.py. 2024-04-23 19:17:41 +04:00
23rd
bea715b41c Added ICCv4 with Little CMS on macOS. 2024-04-23 19:17:41 +04:00
23rd
7be68ca82c Updated Qt to 6.2.8 on macOS. 2024-04-23 19:17:41 +04:00
John Preston
287d5a7413 Fix shadow below Chats/Channels. 2024-04-23 19:17:41 +04:00
John Preston
1448cea01c Fix ripple in active recommendation row. 2024-04-23 19:17:41 +04:00
John Preston
768e8b457b Improve second-time choose of toppeers. 2024-04-23 19:17:41 +04:00
John Preston
05474f4a3f Improve filter switching with toppeers. 2024-04-23 19:17:41 +04:00
John Preston
8acada9b0f Fix toppeers animation with expanded stories. 2024-04-23 19:17:41 +04:00
23rd
a86e7f035f Slightly improved style of inner box for channel earn history entries. 2024-04-23 19:17:41 +04:00
John Preston
9caff93c35 Fix build for Xcode. 2024-04-23 19:13:48 +04:00
John Preston
e6ba85e112 Keyboard navigation in channels/recommendations. 2024-04-23 19:13:48 +04:00
John Preston
046803dbed Fix rotation reading in FFmpeg. 2024-04-23 19:13:48 +04:00
John Preston
615f4b1d1c Ignore pageBlockUnsupported blocks. 2024-04-23 19:13:48 +04:00
John Preston
a11535806d Always show cancel search when focused. 2024-04-23 19:13:48 +04:00
John Preston
34a7169b4f Make unread badges on top peers nicer. 2024-04-23 19:13:47 +04:00
John Preston
4f365c73ad Support active selection in recommendations. 2024-04-23 19:13:47 +04:00
John Preston
705bd9693d Load recommendations on demand. 2024-04-23 19:13:47 +04:00
John Preston
a88770a8ec Don't close recommendations. 2024-04-23 19:13:47 +04:00
John Preston
da423b5bd2 Add a general FastShareLink method. 2024-04-23 19:13:47 +04:00
John Preston
56080bd0e4 Fix boost group link handling for forums. 2024-04-23 19:13:47 +04:00
John Preston
63f66a1369 Show "Boost" button in boost group links. 2024-04-23 19:13:47 +04:00
John Preston
ca1a30196e Fix LinkButton resizing. 2024-04-23 19:13:47 +04:00
John Preston
b2d8e2a1e6 Initial version of channels/recommendations. 2024-04-23 19:13:47 +04:00
John Preston
2ab8bb13c5 Fix links preview. 2024-04-23 19:13:47 +04:00
23rd
3a2c5c6d0a Increased size of stickerset preview in webpages. 2024-04-23 19:13:47 +04:00
23rd
ed13a325e9 Added stickerset preview to webpages. 2024-04-23 19:13:47 +04:00
23rd
f43f99cff2 Added initial api support of stickerset attribute in web pages. 2024-04-23 19:13:47 +04:00
John Preston
a3b8397361 Close chat/media if thrown out by admin. 2024-04-23 19:13:47 +04:00
John Preston
4fb03e532c Optimize stories list generation. 2024-04-23 19:13:47 +04:00
John Preston
645ad5e1bd Update pinned icon in stories list. 2024-04-23 19:13:47 +04:00
John Preston
9036e9e8e3 Show pinned icon in stories. 2024-04-23 19:13:47 +04:00
John Preston
468d8b04d6 Implement stories pin-to-top. 2024-04-23 19:13:47 +04:00
John Preston
4b98ab1246 Fix build with simplified ads. 2024-04-23 19:13:46 +04:00
John Preston
d33e3dc13a Rename Story::pinned to Story::inProfile. 2024-04-23 19:13:46 +04:00
23rd
df16e7c00b Added ability to change reactions limit in channels. 2024-04-23 19:13:46 +04:00
23rd
225c0e0af3 Added initial api support of reactions limit in channels and groups. 2024-04-23 19:13:46 +04:00
23rd
5543927042 Added ability to enable sponsored messages for premium self. 2024-04-23 19:13:46 +04:00
23rd
72b274a2bf Added support of simplified constructor for MTP sponsored message. 2024-04-23 19:13:46 +04:00
John Preston
ac15990b48 Update API scheme to layer178, ads broke build. 2024-04-23 19:13:46 +04:00
John Preston
7387dfdd9c Don't create top/recent peers unnecessary. 2024-04-23 19:13:46 +04:00
John Preston
74a7e7d1b4 Fix non-intentional chats list search focus. 2024-04-23 19:13:46 +04:00
John Preston
a0a9de1d18 Cancel search on switching folders. 2024-04-23 19:13:46 +04:00
John Preston
360366ba9e Fix opening forums from recent peers. 2024-04-23 19:13:46 +04:00
John Preston
0180fe9468 Animate search suggestions. 2024-04-23 19:13:46 +04:00
John Preston
19f5d95a3c Implement keyboard navigation for recent peers. 2024-04-23 19:13:46 +04:00
John Preston
051ca51d3b Allow clearing search results. 2024-04-23 19:13:46 +04:00
John Preston
2a6ff9203b Implement basic recent search results. 2024-04-23 19:13:46 +04:00
John Preston
2e0529bd9a Add data component for RecentPeers. 2024-04-23 19:13:46 +04:00
John Preston
e24ab4f1ab Allow saving emoji to files in debug builds. 2024-04-23 19:13:46 +04:00
John Preston
3dbadeb232 Allow opening top peers in a new window. 2024-04-23 19:13:46 +04:00
John Preston
4cdd939028 Cache topPeers locally. 2024-04-23 19:13:46 +04:00
John Preston
c11f4efc5c Support disabling top peers completely. 2024-04-23 19:13:46 +04:00
John Preston
e1c21b908c Add keyboard navigation to top peers. 2024-04-23 19:13:46 +04:00
John Preston
a6c1def6fe Add ripples to top peers. 2024-04-23 19:13:46 +04:00
John Preston
19ae76d8de Top peers context menu. 2024-04-23 19:13:46 +04:00
John Preston
56e28feb00 Top peer realtime badges. 2024-04-23 19:13:45 +04:00
John Preston
b259c566b7 Top peer unread badges and online indicators. 2024-04-23 19:13:45 +04:00
John Preston
18598f8dca Initial version of top peers. 2024-04-23 19:13:45 +04:00
John Preston
11e4c45969 Don't collapse chats list on window unfocus. 2024-04-23 19:13:45 +04:00
John Preston
39658ffe52 Allow narrow chats width without main section. 2024-04-23 19:13:45 +04:00
John Preston
2f03b9aa29 Make dialogs filter onfocused by default. 2024-04-23 19:13:45 +04:00
John Preston
dc438cff23 Always export file_name in json. 2024-04-23 19:13:45 +04:00
John Preston
18f5521be5 Don't look for SDL when building on macOS. 2024-04-23 19:13:45 +04:00
Ilya Fedin
bae9af1076 Switch crash reporter to system fonts
This should prevent nested crashes when loading custom fonts
2024-04-23 11:01:19 +04:00
Ilya Fedin
8d2805f226 Set font weight in crash reporter 2024-04-23 10:10:43 +04:00
Ilya Fedin
0ad18c8182 Get rid of direct Wayland usage
This should get rid of a very nasty vector of misbehaving and quite a lot of code which is boilerplate over Wayland protocols at the price of losing support of niche features

Most of things served by xcb code on X11 is served by portal code on Wayland, hopefully this tendention will continue and we will be fine just with glib which provides D-Bus and other basic APIs
2024-04-22 11:04:02 +04:00
Ilya Fedin
9046b2cafb Stop using the plasma-shell protocol
Looks like this isn't really correct in combination with xdg-shell
2024-04-22 10:59:16 +04:00
Ilya Fedin
3d994b58a0 ShowXDPOpenWithDialog -> UnsafeShowOpenWith 2024-04-21 03:14:34 +04:00
Ilya Fedin
4310c4978e Fix macOS packaged action 2024-04-19 17:41:07 +04:00
23rd
01bfa46729 Fixed display of default box for message schedule in correspond window. 2024-04-18 11:57:25 +04:00
Kolya
3510ca7184 Don't use/link libsharpyuv in libavif 2024-04-18 09:23:15 +04:00
Ilya Fedin
8d7845daa1 Use --shallow-submodules in Docker 2024-04-18 09:13:37 +04:00
Ilya Fedin
cb5fdac0da Replace mozjpeg with jpegli on Linux 2024-04-17 23:45:15 +04:00
John Preston
78c8d0562b Version 4.16.8: Fix build on Linux. 2024-04-16 23:23:11 +04:00
John Preston
bb0280f096 Revert "Replace mozjpeg with jpegli in snap"
This reverts commit f57e2edf2a.
2024-04-16 23:22:55 +04:00
John Preston
566b53ce9f Revert "Clean remainings of mozjpeg in snapcraft.yml"
This reverts commit da1909cc1d.
2024-04-16 23:22:43 +04:00
John Preston
ec95db0945 Revert "Replace mozjpeg with jpegli in Docker"
This reverts commit a1e2e3d011.
2024-04-16 23:00:08 +04:00
John Preston
a0d7d07543 Version 4.16.8.
- Fix in-app playing of some video and audio files.
- Fix crash on Linux opening chats with custom backgrounds.
- Fix crash on quit after using scheduled messages.
2024-04-16 20:15:32 +04:00
John Preston
fe73bf9053 Fix ripples in channel comments buttons. 2024-04-16 20:14:04 +04:00
John Preston
49c9e08b6c Enforce video/audio extension by mime type. 2024-04-16 20:14:01 +04:00
John Preston
abcf55c498 Check chat background read result. 2024-04-16 20:13:55 +04:00
John Preston
849ce310c4 Jump by keyboard between time input fields. 2024-04-16 20:13:02 +04:00
John Preston
a3e593b747 Fix crash in scheduled messages tear-down. 2024-04-16 20:12:03 +04:00
23rd
5433f95eda Simplified creation of label with custom emoji. 2024-04-16 03:44:00 +03:00
23rd
0459196982 Moved out level meter widget to td_ui. 2024-04-16 03:43:39 +03:00
23rd
6f1457d30d Fixed phrase of placeholder for message edition mode. 2024-04-15 23:56:37 +03:00
23rd
ce596e29c4 Fixed opening message from media player widget in corresponding window. 2024-04-15 23:41:00 +03:00
23rd
86b9d16747 Added additional note for macOS keys to json of custom shortcuts. 2024-04-15 23:30:57 +03:00
John Preston
06911ae42a Version 4.16.7.
- Reimplement file open confirmations.
2024-04-15 17:56:45 +04:00
John Preston
af2cb9b757 Fix possible unhandled exception in highlighting. 2024-04-15 17:56:45 +04:00
John Preston
6a28cd1a35 Reimplement file open confirmations. 2024-04-15 12:07:27 +04:00
23rd
f4a09a9ca0 Fixed display of growth rate in statistics info for huge values. 2024-04-15 11:20:30 +04:00
23rd
a35f020f56 Moved out ScheduledMessages module to components submodule. 2024-04-15 11:20:30 +04:00
23rd
3d48111368 Moved out SponsoredMessages module to components submodule. 2024-04-15 11:20:30 +04:00
23rd
39ed7d7f4c Increased clickable area for each item in EditPeerHistoryVisibilityBox. 2024-04-15 11:20:30 +04:00
23rd
abe83ccb8f Moved inaccessible groups in box for user's own groups to sub-list.
Fixed #27729.
2024-04-15 11:20:30 +04:00
23rd
d1be7c1ff7 Simplified management of PasscodeBox within customCheckCallback. 2024-04-15 11:20:30 +04:00
23rd
1c223e570a Slightly improved code style for long equality expressions. 2024-04-15 11:20:30 +04:00
23rd
a37cbd7d05 Initially refactored statistics module to simplify value types changing. 2024-04-15 11:20:30 +04:00
23rd
7ffa9844e2 Fixed currency formatting for channel earn info section. 2024-04-15 11:20:30 +04:00
23rd
bdf5872f04 Fixed opening channel earn info section for admins. 2024-04-15 11:20:30 +04:00
Ilya Fedin
da1909cc1d Clean remainings of mozjpeg in snapcraft.yml 2024-04-15 10:11:54 +04:00
Ilya Fedin
a503197352 Remove XCBSetDesktopFileName
This is done by Qt nowadays
2024-04-15 10:11:30 +04:00
Nyan
d9d9a8f49d Update data_document_resolver.cpp
Added the LEXE and WLUA extensions to the IsExecutableName function for Windows. Those are registered by default on Windows devices with LUA installed
2024-04-13 23:45:03 +04:00
Ilya Fedin
f57e2edf2a Replace mozjpeg with jpegli in snap 2024-04-11 12:35:47 +04:00
Ilya Fedin
a1e2e3d011 Replace mozjpeg with jpegli in Docker 2024-04-11 12:35:47 +04:00
Ilya Fedin
5e546d1198 Update libjxl in Docker & snap 2024-04-11 12:35:47 +04:00
Ilya Fedin
90405f3ebc Install protobuf in Docker 2024-04-11 12:15:55 +04:00
Nyan
11b57ff7d3 Update data_document_resolver.cpp
The correct python zipapp extension on windows is pyzw, this typo could lead to executing code in the client device without proper warning
2024-04-11 11:24:15 +04:00
Ilya Fedin
fe06cd63ac Lock event loop 2024-04-09 20:48:47 +04:00
John Preston
6c9d5e1499 Improve clipboard QR code image logo quality. 2024-04-09 19:05:02 +04:00
John Preston
6ed910de9f Version 4.16.6.
- Show custom emoji preview on long press.
- Fix resume chat bot button disappearance.
- Fix GIF files playback. (regression in 4.16.3.beta)
2024-04-09 15:20:16 +04:00
John Preston
a506b8b25c Support t.me/username?text=.. links. 2024-04-09 15:20:10 +04:00
John Preston
170ebb57c6 Preview custom emoji on long press from the panel. 2024-04-09 12:11:07 +04:00
John Preston
29dd574e22 Fix toggling chatbot replying, fix saving. 2024-04-09 11:55:52 +04:00
John Preston
72770aa76d Add GIF parser to FFmpeg build.
Fixes #27722.
2024-04-09 11:29:19 +04:00
John Preston
7f1c319aee Add interactive mode to prepare script. 2024-04-09 11:29:16 +04:00
John Preston
60805bd916 Version 4.16.5.
- Fix editing privacy for groups and channels invitations.
- Possible fix for the network unresponsiveness after sleep.
- Possible fix for wide range of memory allocator crashes on Linux.
2024-04-08 22:31:07 +04:00
Ilya Fedin
10272ee0cf Revert "Replace jemalloc with scudo"
This reverts commit 960761ef37.
2024-04-08 21:57:25 +04:00
John Preston
7c44cda76e Fix premium users deselection in multi-select field. 2024-04-08 21:56:56 +04:00
John Preston
0152d2c48e Fix possible temp auth key bind freeze. 2024-04-08 21:56:56 +04:00
23rd
c5febce548 Moved out some functions for main menu to separated files. 2024-04-08 17:34:58 +03:00
23rd
129b07c2c0 Improved style of box for all of user's own groups and channels. 2024-04-08 17:02:52 +03:00
23rd
47bf099b88 Fixed updating of field placeholder in compose controls while editing. 2024-04-08 06:55:44 +03:00
23rd
94f1d23788 Fixed behavior to open section with scheduled messages at top. 2024-04-08 04:12:14 +03:00
Ilya Fedin
84ce72ec7a Fix a crash due to half-hidden media viewer 2024-04-07 14:28:51 +04:00
Ilya Fedin
3da8351522 Let Wayland to use QGuiApplication::screenAt (forgotten place) 2024-04-07 14:08:40 +04:00
Ilya Fedin
542153d950 Use nasm from repository 2024-04-07 11:20:46 +04:00
Ilya Fedin
090fdfb458 Add asan libraries to Docker image 2024-04-07 11:20:46 +04:00
John Preston
77835a43a5 Version 4.16.4: Update patches on Linux. 2024-04-07 11:19:23 +04:00
John Preston
1a2a1f1c17 Version 4.16.4.
- Bug fixes and other minor improvements.
2024-04-06 22:59:03 +04:00
John Preston
eaaa704fa4 Check the URL in IV like in attach bots. 2024-04-06 22:59:03 +04:00
John Preston
7803f8e670 Simplify escaping when parsing IV. 2024-04-06 22:59:03 +04:00
Sergey A. Osokin
f36e3c213e Fix build on FreeBSD 13.3 / llvm 17.0.6 / qt 5.15.13 2024-04-06 20:30:08 +04:00
John Preston
6fb1e23ed5 Fix file dialog filter for videos from attach menu. 2024-04-06 13:05:12 +04:00
John Preston
86d0c49e44 Add "Boost Group/Channel" button to chat menu. 2024-04-06 13:05:12 +04:00
John Preston
9251e6faba Use better title in boost box. 2024-04-06 13:05:12 +04:00
John Preston
8f8725e1af Beta version 4.16.3: Fix dav1d in FFmpeg on macOS. 2024-04-05 21:25:20 +04:00
John Preston
0ce6a4cbdb Beta version 4.16.3: Re-enable system proxy (Linux).
This reverts commit 1e6fb202f0.
2024-04-05 20:48:06 +04:00
John Preston
ad3f705f50 Beta version 4.16.3.
- Improve media upload speed.
- Update FFmpeg to 6.1.1.
2024-04-05 15:35:01 +04:00
John Preston
c5847caa91 Update FFmpeg to 6.1.1: Fix AV1 videos. 2024-04-05 14:42:29 +04:00
John Preston
8df6d9db7e Don't update server-time too frequently. 2024-04-05 09:51:20 +04:00
John Preston
a9c1970f41 Send up to 1MB of parts to a single session. 2024-04-05 09:51:20 +04:00
John Preston
c3f0d2ef31 Adaptive upload up to 8 sessions. 2024-04-05 09:51:20 +04:00
John Preston
0dcc439dda Allow upload requests for several files at once. 2024-04-05 09:51:20 +04:00
John Preston
5b0cac47ad Reapply "Use plain vector for prepared upload parts."
This reverts commit f6f8eefaa0.
2024-04-05 09:51:20 +04:00
John Preston
b39e78a4a9 Reapply "Remove SendMediaReady legacy helper."
This reverts commit 09f07a7a9d.
2024-04-05 09:51:20 +04:00
478 changed files with 12819 additions and 23879 deletions

View File

@@ -1,32 +1,11 @@
name: No Response
name: Can't reproduce.
# Both `issue_comment` and `scheduled` event types are required for this Action
# to work properly.
on:
issue_comment:
types: [created]
schedule:
- cron: '0 0 * * *'
- cron: '0 3 * * *'
jobs:
waiting-for-answer:
runs-on: ubuntu-latest
steps:
- uses: lee-dohm/no-response@v0.5.0
with:
token: ${{ github.token }}
responseRequiredLabel: waiting for answer
needs-user-action:
runs-on: ubuntu-latest
steps:
- uses: lee-dohm/no-response@v0.5.0
with:
token: ${{ github.token }}
responseRequiredLabel: needs user action
cant-reproduce:
if: github.event_name != 'issue_comment'
runs-on: ubuntu-latest
steps:
- uses: lee-dohm/no-response@v0.5.0

View File

@@ -49,7 +49,6 @@ jobs:
defines:
- ""
- "DESKTOP_APP_DISABLE_X11_INTEGRATION"
- "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION"
env:
UPLOAD_ARTIFACT: "true"

View File

@@ -49,7 +49,7 @@ jobs:
env:
GIT: "https://github.com"
OPENALDIR: "/usr/local/opt/openal-soft"
CMAKE_PREFIX_PATH: "/usr/local/opt/ffmpeg@6:/usr/local/opt/openal-soft"
UPLOAD_ARTIFACT: "true"
ONLY_CACHE: "false"
MANUAL_CACHING: "1"
@@ -69,7 +69,7 @@ jobs:
run: |
brew update
brew upgrade || true
brew install autoconf automake boost cmake ffmpeg openal-soft openssl opus ninja pkg-config python qt yasm xz
brew install autoconf automake boost cmake ffmpeg@6 openal-soft openssl opus ninja pkg-config python qt yasm xz
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
xcodebuild -version > CACHE_KEY.txt

16
.github/workflows/needs-user-action.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: Needs user action.
on:
issue_comment:
types: [created]
schedule:
- cron: '0 2 * * *'
jobs:
needs-user-action:
runs-on: ubuntu-latest
steps:
- uses: lee-dohm/no-response@v0.5.0
with:
token: ${{ github.token }}
responseRequiredLabel: needs user action

View File

@@ -64,7 +64,7 @@ jobs:
uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8
- name: Telegram Desktop snap build.
run: sg lxd -c 'snap run snapcraft -v'
run: sg lxd -c 'snap run snapcraft --verbosity=debug'
- name: Move artifact.
if: env.UPLOAD_ARTIFACT == 'true'

View File

@@ -0,0 +1,16 @@
name: Waiting for answer.
on:
issue_comment:
types: [created]
schedule:
- cron: '0 0 * * *'
jobs:
waiting-for-answer:
runs-on: ubuntu-latest
steps:
- uses: lee-dohm/no-response@v0.5.0
with:
token: ${{ github.token }}
responseRequiredLabel: waiting for answer

12
.gitmodules vendored
View File

@@ -76,15 +76,12 @@
[submodule "Telegram/lib_webview"]
path = Telegram/lib_webview
url = https://github.com/desktop-app/lib_webview.git
[submodule "Telegram/ThirdParty/jemalloc"]
path = Telegram/ThirdParty/jemalloc
url = https://github.com/jemalloc/jemalloc
[submodule "Telegram/ThirdParty/dispatch"]
path = Telegram/ThirdParty/dispatch
url = https://github.com/apple/swift-corelibs-libdispatch
[submodule "Telegram/ThirdParty/plasma-wayland-protocols"]
path = Telegram/ThirdParty/plasma-wayland-protocols
url = https://github.com/KDE/plasma-wayland-protocols.git
[submodule "Telegram/ThirdParty/wayland-protocols"]
path = Telegram/ThirdParty/wayland-protocols
url = https://github.com/gitlab-freedesktop-mirrors/wayland-protocols.git
[submodule "Telegram/ThirdParty/kimageformats"]
path = Telegram/ThirdParty/kimageformats
url = https://github.com/KDE/kimageformats.git
@@ -94,9 +91,6 @@
[submodule "Telegram/ThirdParty/cld3"]
path = Telegram/ThirdParty/cld3
url = https://github.com/google/cld3.git
[submodule "Telegram/ThirdParty/wayland"]
path = Telegram/ThirdParty/wayland
url = https://github.com/gitlab-freedesktop-mirrors/wayland.git
[submodule "Telegram/ThirdParty/libprisma"]
path = Telegram/ThirdParty/libprisma
url = https://github.com/desktop-app/libprisma.git

View File

@@ -62,7 +62,7 @@ if (NOT DESKTOP_APP_USE_PACKAGED)
if (WIN32)
set(qt_version 5.15.13)
elseif (APPLE)
set(qt_version 6.2.7)
set(qt_version 6.2.8)
endif()
endif()
include(cmake/external/qt/package.cmake)

View File

@@ -273,6 +273,8 @@ PRIVATE
boxes/local_storage_box.h
boxes/max_invite_box.cpp
boxes/max_invite_box.h
boxes/moderate_messages_box.cpp
boxes/moderate_messages_box.h
boxes/peer_list_box.cpp
boxes/peer_list_box.h
boxes/peer_list_controllers.cpp
@@ -460,6 +462,14 @@ PRIVATE
data/business/data_business_info.h
data/business/data_shortcut_messages.cpp
data/business/data_shortcut_messages.h
data/components/recent_peers.cpp
data/components/recent_peers.h
data/components/scheduled_messages.cpp
data/components/scheduled_messages.h
data/components/sponsored_messages.cpp
data/components/sponsored_messages.h
data/components/top_peers.cpp
data/components/top_peers.h
data/notify/data_notify_settings.cpp
data/notify/data_notify_settings.h
data/notify/data_peer_notify_settings.cpp
@@ -577,14 +587,10 @@ PRIVATE
data/data_send_action.h
data/data_session.cpp
data/data_session.h
data/data_scheduled_messages.cpp
data/data_scheduled_messages.h
data/data_shared_media.cpp
data/data_shared_media.h
data/data_sparse_ids.cpp
data/data_sparse_ids.h
data/data_sponsored_messages.cpp
data/data_sponsored_messages.h
data/data_statistics.h
data/data_stories.cpp
data/data_stories.h
@@ -608,6 +614,18 @@ PRIVATE
data/data_wall_paper.h
data/data_web_page.cpp
data/data_web_page.h
dialogs/ui/dialogs_layout.cpp
dialogs/ui/dialogs_layout.h
dialogs/ui/dialogs_message_view.cpp
dialogs/ui/dialogs_message_view.h
dialogs/ui/dialogs_stories_content.cpp
dialogs/ui/dialogs_stories_content.h
dialogs/ui/dialogs_suggestions.cpp
dialogs/ui/dialogs_suggestions.h
dialogs/ui/dialogs_topics_view.cpp
dialogs/ui/dialogs_topics_view.h
dialogs/ui/dialogs_video_userpic.cpp
dialogs/ui/dialogs_video_userpic.h
dialogs/dialogs_entry.cpp
dialogs/dialogs_entry.h
dialogs/dialogs_indexed_list.cpp
@@ -630,16 +648,6 @@ PRIVATE
dialogs/dialogs_search_tags.h
dialogs/dialogs_widget.cpp
dialogs/dialogs_widget.h
dialogs/ui/dialogs_layout.cpp
dialogs/ui/dialogs_layout.h
dialogs/ui/dialogs_message_view.cpp
dialogs/ui/dialogs_message_view.h
dialogs/ui/dialogs_stories_content.cpp
dialogs/ui/dialogs_stories_content.h
dialogs/ui/dialogs_topics_view.cpp
dialogs/ui/dialogs_topics_view.h
dialogs/ui/dialogs_video_userpic.cpp
dialogs/ui/dialogs_video_userpic.h
editor/color_picker.cpp
editor/color_picker.h
editor/controllers/controllers.h
@@ -1199,11 +1207,6 @@ PRIVATE
payments/payments_checkout_process.h
payments/payments_form.cpp
payments/payments_form.h
platform/linux/linux_wayland_integration_dummy.cpp
platform/linux/linux_wayland_integration.cpp
platform/linux/linux_wayland_integration.h
platform/linux/linux_xdp_open_with_dialog.cpp
platform/linux/linux_xdp_open_with_dialog.h
platform/linux/file_utilities_linux.cpp
platform/linux/file_utilities_linux.h
platform/linux/launcher_linux.cpp
@@ -1459,8 +1462,6 @@ PRIVATE
ui/image/image_location.h
ui/image/image_location_factory.cpp
ui/image/image_location_factory.h
ui/widgets/level_meter.cpp
ui/widgets/level_meter.h
ui/countryinput.cpp
ui/countryinput.h
ui/dynamic_thumbnails.cpp
@@ -1476,6 +1477,8 @@ PRIVATE
ui/search_field_controller.h
ui/text/format_song_document_name.cpp
ui/text/format_song_document_name.h
ui/widgets/label_with_custom_emoji.cpp
ui/widgets/label_with_custom_emoji.h
ui/unread_badge.cpp
ui/unread_badge.h
window/main_window.cpp
@@ -1503,6 +1506,8 @@ PRIVATE
window/window_lock_widgets.h
window/window_main_menu.cpp
window/window_main_menu.h
window/window_main_menu_helpers.cpp
window/window_main_menu_helpers.h
window/window_media_preview.cpp
window/window_media_preview.h
window/window_peer_menu.cpp
@@ -1554,16 +1559,6 @@ if (NOT build_winstore)
)
endif()
if (DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
remove_target_sources(Telegram ${src_loc}
platform/linux/linux_wayland_integration.cpp
)
else()
remove_target_sources(Telegram ${src_loc}
platform/linux/linux_wayland_integration_dummy.cpp
)
endif()
if (DESKTOP_APP_USE_PACKAGED)
remove_target_sources(Telegram ${src_loc}
platform/mac/mac_iconv_helper.c
@@ -1701,19 +1696,6 @@ else()
desktop-app::external_xcb
)
endif()
if (NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
qt_generate_wayland_protocol_client_sources(Telegram
FILES
${third_party_loc}/wayland/protocol/wayland.xml
${third_party_loc}/plasma-wayland-protocols/src/protocols/plasma-shell.xml
)
target_link_libraries(Telegram
PRIVATE
desktop-app::external_wayland_client
)
endif()
endif()
if (build_macstore)

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -134,9 +134,32 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover {
.page-slide {
position: relative;
width: 100%;
min-height: 100%;
margin-left: 0%;
transition: margin 240ms ease-in-out;
}
.page-footer {
height: 32px;
margin-top: -32px;
background: var(--td-window-bg-over);
}
.page-footer .content {
padding: 3px 18px;
font-size: 15px;
color: var(--td-window-sub-text-fg);
text-align: center;
}
.page-footer .wrong {
position: relative;
padding: 5px;
margin: -5px;
color: var(--td-window-sub-text-fg);
text-decoration: none;
cursor: pointer;
}
.page-footer .wrong:hover {
text-decoration: underline;
}
.hidden-left,
.hidden-right {
pointer-events: none;
@@ -148,7 +171,7 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover {
margin-left: 100%;
}
article {
padding-bottom: 12px;
padding-bottom: 40px;
overflow-y: hidden;
overflow-x: auto;
white-space: pre-wrap;
@@ -893,6 +916,9 @@ section.related a.related-link:after {
right: 0;
bottom: 0;
}
section.related a.related-link:last-child:after {
border-bottom: 0px;
}
section.related .related-link-url {
display: block;
font-size: 15px;
@@ -1027,6 +1053,9 @@ section.channel > a > h4 {
display: block;
margin: 0 auto;
}
.media-outer {
margin-bottom: 16px;
}
.photo-wrap,
.video-wrap {
width: 100%;

View File

@@ -26,7 +26,7 @@ var IV = {
}
target = target.parentNode;
}
if (!target || !target.hasAttribute('href')) {
if (!target || (context === '' && !target.hasAttribute('href'))) {
return;
}
var base = document.createElement('A');
@@ -413,9 +413,12 @@ var IV = {
var article = function (el) {
return el.getElementsByTagName('article')[0];
};
var from = article(IV.findPageScroll());
var to = article(IV.makeScrolledContent(data.html));
morphdom(from, to, {
var footer = function (el) {
return el.getElementsByClassName('page-footer')[0];
};
var from = IV.findPageScroll();
var to = IV.makeScrolledContent(data.html);
morphdom(article(from), article(to), {
onBeforeElUpdated: function (fromEl, toEl) {
if (fromEl.classList.contains('video')
&& toEl.classList.contains('video')
@@ -439,6 +442,7 @@ var IV = {
return !fromEl.isEqualNode(toEl);
}
});
morphdom(footer(from), footer(to));
IV.initMedia();
eval(data.js);
},
@@ -477,9 +481,7 @@ var IV = {
var result = document.createElement('div');
result.className = 'page-scroll';
result.tabIndex = '-1';
result.innerHTML = '<div class="page-slide"><article>'
+ html
+ '</article></div>';
result.innerHTML = html.trim();
result.onscroll = IV.frameScrolled;
return result;
},

View File

@@ -309,7 +309,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_limit_reached#one" = "You've reached the message text limit. Please make the text shorter by {count} character.";
"lng_edit_limit_reached#other" = "You've reached the message text limit. Please make the text shorter by {count} characters.";
"lng_edit_message" = "Edit message";
"lng_edit_message_text" = "New message text...";
"lng_edit_message_text" = "Caption";
"lng_deleted" = "Deleted Account";
"lng_deleted_message" = "Deleted message";
"lng_deleted_story" = "Deleted story";
@@ -730,10 +730,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_angle_backend_d3d11on12" = "D3D11on12";
"lng_settings_angle_backend_opengl" = "OpenGL";
"lng_settings_angle_backend_disabled" = "Disabled";
"lng_settings_top_peers_title" = "Frequent contacts";
"lng_settings_top_peers_suggest" = "Suggest frequent contacts";
"lng_settings_top_peers_about" = "Display people you message frequently at the top of the search section for quick access.";
"lng_settings_sensitive_title" = "Sensitive content";
"lng_settings_sensitive_disable_filtering" = "Disable filtering";
"lng_settings_sensitive_about" = "Display sensitive media in public channels on all your Telegram devices.";
"lng_settings_security_bots" = "Bots and websites";
"lng_settings_file_confirmations" = "File open confirmations";
"lng_settings_edit_extensions" = "Extensions whitelist";
"lng_settings_edit_extensions_about" = "Open files with the following extensions without additional confirmation.";
"lng_settings_edit_ip_confirm" = "IP reveal warning";
"lng_settings_edit_ip_confirm_about" = "Show confirmation when opening files that may reveal your IP address.";
"lng_settings_clear_payment_info" = "Clear Payment and Shipping Info";
"lng_settings_logged_in" = "Connected websites";
"lng_settings_logged_in_title" = "Logged in with Telegram";
@@ -835,6 +843,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_auto_night_mode_on" = "System";
"lng_settings_auto_night_warning" = "You have enabled auto-night mode. If you want to change the dark mode settings, you'll need to disable it first.";
"lng_settings_auto_night_disable" = "Disable";
"lng_settings_font_family" = "Font family";
"lng_settings_color_title" = "Color preview";
"lng_settings_color_reply" = "Reply to your message";
@@ -1487,6 +1496,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_manage_peer_reactions_none_about" = "Members of the group can't add any reactions to messages.";
"lng_manage_peer_reactions_some_title" = "Only allow these reactions";
"lng_manage_peer_reactions_available" = "Available reactions";
"lng_manage_peer_reactions_available_ph" = "Add reactions...";
"lng_manage_peer_reactions_own" = "You can also {link} emoji packs and use them as reactions.";
"lng_manage_peer_reactions_own_link" = "create your own";
"lng_manage_peer_reactions_level#one" = "Your channel needs to reach level **{count}** to use **{same_count}** custom reaction.";
@@ -1495,6 +1505,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_manage_peer_reactions_boost_link" = "here";
"lng_manage_peer_reactions_limit" = "Channels can't have more custom reactions.";
"lng_manage_peer_reactions_max_title" = "Maximum number of reactions";
"lng_manage_peer_reactions_max_slider#one" = "{count} reaction per post";
"lng_manage_peer_reactions_max_slider#other" = "{count} reactions per post";
"lng_manage_peer_reactions_max_about" = "Limit the number of different reactions that can be added to a post, including already published ones.";
"lng_manage_peer_antispam" = "Aggressive Anti-Spam";
"lng_manage_peer_antispam_about" = "Telegram will filter more spam but may occasionally affect ordinary messages. You can report False Positives in Recent Actions.";
"lng_manage_peer_antispam_not_enough#one" = "Aggressive filtering can be enabled only in groups with more than **{count} member**.";
@@ -2264,6 +2279,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_business_about_chat_intro" = "Customize the message people see before they start a chat with you.";
"lng_business_subtitle_chat_links" = "Links to Chat";
"lng_business_about_chat_links" = "Create links that start a chat with you, suggesting the first message.";
"lng_business_subtitle_sponsored" = "Ads in Channels";
"lng_business_button_sponsored" = "Do Not Hide Ads";
"lng_business_about_sponsored" = "As a Premium subscriber, you dont see any ads on Telegram, but you can turn them on, for example, to view your own ads that you launched on the {link}";
"lng_business_about_sponsored_link" = "Telegram Ad Platform {emoji}";
"lng_business_about_sponsored_url" = "https://ads.telegram.org";
"lng_location_title" = "Location";
"lng_location_about" = "Display the location of your business on your account.";
@@ -2834,7 +2854,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_in_dlg_audio_count#other" = "{count} audio";
"lng_ban_user" = "Ban User";
"lng_ban_users" = "Ban users";
"lng_restrict_users" = "Restrict users";
"lng_delete_all_from_user" = "Delete all from {user}";
"lng_delete_all_from_users" = "Delete all from users";
"lng_restrict_user_part" = "Partially restrict this user {emoji}";
"lng_restrict_users_part" = "Partially restrict users {emoji}";
"lng_restrict_user_full" = "Fully ban this user {emoji}";
"lng_restrict_users_full" = "Fully ban users {emoji}";
"lng_restrict_users_part_single_header" = "What can this user do?";
"lng_restrict_users_part_header#one" = "What can {count} selected user do?";
"lng_restrict_users_part_header#other" = "What can {count} selected users do?";
"lng_report_spam" = "Report Spam";
"lng_report_spam_and_leave" = "Report spam and leave";
"lng_report_spam_done" = "Thank you for your report.";
@@ -3062,6 +3092,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_unread_bar_some" = "Unread messages";
"lng_maps_point" = "Location";
"lng_live_location" = "Live Location";
"lng_live_location_now" = "updated just now";
"lng_live_location_minutes#one" = "updated {count} minute ago";
"lng_live_location_minutes#other" = "updated {count} minutes ago";
"lng_live_location_hours#one" = "updated {count} hour ago";
"lng_live_location_hours#other" = "updated {count} hours ago";
"lng_live_location_today" = "updated today at {time}";
"lng_live_location_yesterday" = "updated yesterday at {time}";
"lng_live_location_date_time" = "updated {date} at {time}";
"lng_save_photo" = "Save image";
"lng_save_video" = "Save video";
"lng_save_audio_file" = "Save audio file";
@@ -3405,6 +3444,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_mediaview_forward" = "Forward";
"lng_mediaview_delete" = "Delete";
"lng_mediaview_save_to_profile" = "Save to Profile";
"lng_mediaview_pin_story_done" = "Story pinned";
"lng_mediaview_pin_story_about" = "Now it will be always shown on the top.";
"lng_mediaview_pin_stories_done#one" = "{count} story pinned";
"lng_mediaview_pin_stories_done#other" = "{count} stories pinned";
"lng_mediaview_pin_stories_about#one" = "Now it will be always shown on the top.";
"lng_mediaview_pin_stories_about#other" = "Now they will be always shown on the top.";
"lng_mediaview_unpin_story_done" = "Story unpinned.";
"lng_mediaview_unpin_stories_done#one" = "{count} story unpinned";
"lng_mediaview_unpin_stories_done#other" = "{count} stories unpinned";
"lng_mediaview_pin_limit#one" = "You can't pin more than {count} story.";
"lng_mediaview_pin_limit#other" = "You can't pin more than {count} stories.";
"lng_mediaview_archive_story" = "Archive Story";
"lng_mediaview_photos_all" = "View all photos";
"lng_mediaview_files_all" = "View all files";
@@ -4480,10 +4530,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_translate_settings_about" = "The 'Translate' button will appear when you open a context menu on a text message.";
"lng_translate_settings_one" = "Please choose at least one language so that it can be used as the \"Translate to\" language.";
"lng_launch_exe_warning" = "This file has a {extension} extension.\nAre you sure you want to run it?";
"lng_launch_exe_warning" = "This file has {extension} extension.\nAre you sure you want to run it?";
"lng_launch_other_warning" = "This file has {extension} extension.\nAre you sure you want to open it?";
"lng_launch_svg_warning" = "Opening this file can potentially expose your IP address to its sender. Continue?";
"lng_launch_exe_sure" = "Run";
"lng_launch_other_sure" = "Open";
"lng_launch_exe_dont_ask" = "Don't ask me again";
"lng_launch_dont_ask" = "Remember for this file type";
"lng_launch_dont_ask_settings" = "You can later edit trusted file types in Settings > Privacy and Security > File open confirmations.";
"lng_polls_anonymous" = "Anonymous Poll";
"lng_polls_public" = "Poll";
@@ -4695,6 +4749,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_view_button_boost" = "Boost";
"lng_view_button_giftcode" = "Open";
"lng_view_button_iv" = "Instant View";
"lng_view_button_stickerset" = "View stickers";
"lng_view_button_emojipack" = "View emoji";
"lng_sponsored_hide_ads" = "Hide";
"lng_sponsored_title" = "What are sponsored messages?";
@@ -5064,6 +5120,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_iv_share" = "Share";
"lng_iv_join_channel" = "Join";
"lng_iv_window_title" = "Instant View";
"lng_iv_wrong_layout" = "Wrong layout?";
"lng_limit_download_title" = "Download speed limited";
"lng_limit_download_subscribe" = "Subscribe to {link} and increase download speed {increase}.";
@@ -5078,6 +5135,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_limit_upload_increase_speed" = "by **{percent}**";
"lng_limit_upload_subscribe_link" = "Telegram Premium";
"lng_recent_frequent" = "Frequent contacts";
"lng_recent_frequent_all" = "Show all";
"lng_recent_frequent_collapse" = "Collapse";
"lng_recent_title" = "Recent";
"lng_recent_clear" = "Clear";
"lng_recent_clear_sure" = "Do you want to clear your search history?";
"lng_recent_remove" = "Remove from Recent";
"lng_recent_clear_all" = "Clear all";
"lng_recent_hide_top" = "Remove all & Disable";
"lng_recent_hide_sure" = "Are you sure you want to clear and disable frequent contacts list?\n\nYou can always turn this feature back on in Settings > Privacy > Suggest Frequent Contacts.";
"lng_recent_hide_button" = "Hide";
"lng_recent_none" = "Recent search results\nwill appear here.";
"lng_recent_chats" = "Chats";
"lng_recent_channels" = "Channels";
"lng_channels_none_title" = "No channels yet...";
"lng_channels_none_about" = "You are not currently subscribed to any channels.";
"lng_channels_your_title" = "Channels you joined";
"lng_channels_your_more" = "Show more";
"lng_channels_your_less" = "Show less";
"lng_channels_recommended" = "Recommended channels";
"lng_font_box_title" = "Choose font family";
"lng_font_default" = "Default";
"lng_font_system" = "System font";
"lng_font_not_found" = "Font not found.";
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";

View File

@@ -24,5 +24,7 @@
<file alias="chat_link.tgs">../../animations/chat_link.tgs</file>
<file alias="collectible_username.tgs">../../animations/collectible_username.tgs</file>
<file alias="collectible_phone.tgs">../../animations/collectible_phone.tgs</file>
<file alias="search.tgs">../../animations/search.tgs</file>
<file alias="noresults.tgs">../../animations/noresults.tgs</file>
</qresource>
</RCC>

View File

@@ -10,7 +10,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="4.16.2.0" />
Version="5.0.0.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View File

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

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4,16,2,0
PRODUCTVERSION 4,16,2,0
FILEVERSION 5,0,0,0
PRODUCTVERSION 5,0,0,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", "4.16.2.0"
VALUE "FileVersion", "5.0.0.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "4.16.2.0"
VALUE "ProductVersion", "5.0.0.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -236,14 +236,14 @@ void SendBotCallbackDataWithPassword(
} else {
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) {
const Core::CloudPasswordResult &result,
QPointer<PasscodeBox> box) {
if (const auto button = getButton()) {
if (button->requestId) {
return;
@@ -257,18 +257,17 @@ void SendBotCallbackDataWithPassword(
return;
}
SendBotCallbackData(strongController, item, row, column, result, [=] {
if (*box) {
(*box)->closeBox();
if (box) {
box->closeBox();
}
}, [=](const QString &error) {
if (*box) {
(*box)->handleCustomCheckError(error);
if (box) {
box->handleCustomCheckError(error);
}
});
}
};
auto object = Box<PasscodeBox>(session, fields);
*box = Ui::MakeWeak(object.data());
show->showBox(std::move(object), Ui::LayerOption::CloseOther);
}, *lifetime);
}

View File

@@ -112,8 +112,8 @@ void ApplyLastList(
channel->mgInfo->lastAdmins.clear();
channel->mgInfo->lastRestricted.clear();
channel->mgInfo->lastParticipants.clear();
channel->mgInfo->lastParticipantsStatus =
MegagroupInfo::LastParticipantsUpToDate
channel->mgInfo->lastParticipantsStatus
= MegagroupInfo::LastParticipantsUpToDate
| MegagroupInfo::LastParticipantsOnceReceived;
auto botStatus = channel->mgInfo->botStatus;
@@ -212,7 +212,7 @@ void ApplyBotsList(
}
[[nodiscard]] ChatParticipants::Channels ParseSimilar(
not_null<ChannelData*> channel,
not_null<Main::Session*> session,
const MTPmessages_Chats &chats) {
auto result = ChatParticipants::Channels();
std::vector<not_null<ChannelData*>>();
@@ -220,13 +220,13 @@ void ApplyBotsList(
const auto &list = data.vchats().v;
result.list.reserve(list.size());
for (const auto &chat : list) {
const auto peer = channel->owner().processChat(chat);
const auto peer = session->data().processChat(chat);
if (const auto channel = peer->asChannel()) {
result.list.push_back(channel);
}
}
if constexpr (MTPDmessages_chatsSlice::Is<decltype(data)>()) {
if (channel->session().premiumPossible()) {
if (session->premiumPossible()) {
result.more = data.vcount().v - data.vchats().v.size();
}
}
@@ -234,6 +234,12 @@ void ApplyBotsList(
return result;
}
[[nodiscard]] ChatParticipants::Channels ParseSimilar(
not_null<ChannelData*> channel,
const MTPmessages_Chats &chats) {
return ParseSimilar(&channel->session(), chats);
}
} // namespace
ChatParticipant::ChatParticipant(
@@ -351,7 +357,8 @@ QString ChatParticipant::rank() const {
}
ChatParticipants::ChatParticipants(not_null<ApiWrap*> api)
: _api(&api->instance()) {
: _session(&api->session())
, _api(&api->instance()) {
}
void ChatParticipants::requestForAdd(
@@ -585,6 +592,33 @@ ChatParticipants::Parsed ChatParticipants::ParseRecent(
return result;
}
void ChatParticipants::Restrict(
not_null<ChannelData*> channel,
not_null<PeerData*> participant,
ChatRestrictionsInfo oldRights,
ChatRestrictionsInfo newRights,
Fn<void()> onDone,
Fn<void()> onFail) {
channel->session().api().request(MTPchannels_EditBanned(
channel->inputChannel,
participant->input,
MTP_chatBannedRights(
MTP_flags(MTPDchatBannedRights::Flags::from_raw(
uint32(newRights.flags))),
MTP_int(newRights.until))
)).done([=](const MTPUpdates &result) {
channel->session().api().applyUpdates(result);
channel->applyEditBanned(participant, oldRights, newRights);
if (onDone) {
onDone();
}
}).fail([=] {
if (onFail) {
onFail();
}
}).send();
}
void ChatParticipants::requestSelf(not_null<ChannelData*> channel) {
if (_selfParticipantRequests.contains(channel)) {
return;
@@ -730,8 +764,11 @@ void ChatParticipants::loadSimilarChannels(not_null<ChannelData*> channel) {
return;
}
}
using Flag = MTPchannels_GetChannelRecommendations::Flag;
_similar[channel].requestId = _api.request(
MTPchannels_GetChannelRecommendations(channel->inputChannel)
MTPchannels_GetChannelRecommendations(
MTP_flags(Flag::f_channel),
channel->inputChannel)
).done([=](const MTPmessages_Chats &result) {
auto &similar = _similar[channel];
similar.requestId = 0;
@@ -766,4 +803,29 @@ auto ChatParticipants::similarLoaded() const
return _similarLoaded.events();
}
void ChatParticipants::loadRecommendations() {
if (_recommendationsLoaded.current() || _recommendations.requestId) {
return;
}
_recommendations.requestId = _api.request(
MTPchannels_GetChannelRecommendations(
MTP_flags(0),
MTP_inputChannelEmpty())
).done([=](const MTPmessages_Chats &result) {
_recommendations.requestId = 0;
auto parsed = ParseSimilar(_session, result);
_recommendations.channels = std::move(parsed);
_recommendations.channels.more = 0;
_recommendationsLoaded = true;
}).send();
}
const ChatParticipants::Channels &ChatParticipants::recommendations() const {
return _recommendations.channels;
}
rpl::producer<> ChatParticipants::recommendationsLoaded() const {
return _recommendationsLoaded.changes() | rpl::to_empty;
}
} // namespace Api

View File

@@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class ApiWrap;
class ChannelData;
namespace Main {
class Session;
} // namespace Main
namespace Ui {
class Show;
} // namespace Ui
@@ -96,6 +100,13 @@ public:
static Parsed ParseRecent(
not_null<ChannelData*> channel,
const TLMembers &data);
static void Restrict(
not_null<ChannelData*> channel,
not_null<PeerData*> participant,
ChatRestrictionsInfo oldRights,
ChatRestrictionsInfo newRights,
Fn<void()> onDone,
Fn<void()> onFail);
void add(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
@@ -134,12 +145,18 @@ public:
[[nodiscard]] auto similarLoaded() const
-> rpl::producer<not_null<ChannelData*>>;
void loadRecommendations();
[[nodiscard]] const Channels &recommendations() const;
[[nodiscard]] rpl::producer<> recommendationsLoaded() const;
private:
struct SimilarChannels {
Channels channels;
mtpRequestId requestId = 0;
};
const not_null<Main::Session*> _session;
MTP::Sender _api;
using PeerRequests = base::flat_map<PeerData*, mtpRequestId>;
@@ -165,6 +182,9 @@ private:
base::flat_map<not_null<ChannelData*>, SimilarChannels> _similar;
rpl::event_stream<not_null<ChannelData*>> _similarLoaded;
SimilarChannels _recommendations;
rpl::variable<bool> _recommendationsLoaded = false;
};
} // namespace Api

View File

@@ -62,6 +62,10 @@ void ConfirmPhone::resolve(
return bad("FirebaseSms");
}, [&](const MTPDauth_sentCodeTypeEmailCode &) {
return bad("EmailCode");
}, [&](const MTPDauth_sentCodeTypeSmsWord &) {
return bad("SmsWord");
}, [&](const MTPDauth_sentCodeTypeSmsPhrase &) {
return bad("SmsPhrase");
}, [&](const MTPDauth_sentCodeTypeSetUpEmailRequired &) {
return bad("SetUpEmailRequired");
});

View File

@@ -58,25 +58,33 @@ void HandleWithdrawalButton(
state->loading = false;
auto fields = PasscodeBox::CloudFields::From(pass);
fields.customTitle =
tr::lng_channel_earn_balance_password_title();
fields.customDescription =
tr::lng_channel_earn_balance_password_description(tr::now);
fields.customTitle
= tr::lng_channel_earn_balance_password_title();
fields.customDescription
= tr::lng_channel_earn_balance_password_description(tr::now);
fields.customSubmitButton = tr::lng_passcode_submit();
fields.customCheckCallback = crl::guard(button, [=](
const Core::CloudPasswordResult &result) {
const Core::CloudPasswordResult &result,
QPointer<PasscodeBox> box) {
const auto done = [=](const QString &result) {
if (!result.isEmpty()) {
UrlClickHandler::Open(result);
if (box) {
box->closeBox();
}
}
};
const auto fail = [=](const QString &error) {
show->showToast(error);
};
session->api().request(
MTPstats_GetBroadcastRevenueWithdrawalUrl(
channel->inputChannel,
result.result
)).done([=](const MTPstats_BroadcastRevenueWithdrawalUrl &r) {
const auto url = qs(r.data().vurl());
if (!url.isEmpty()) {
UrlClickHandler::Open(url);
}
done(qs(r.data().vurl()));
}).fail([=](const MTP::Error &error) {
show->showToast(error.type());
fail(error.type());
}).send();
});
show->show(Box<PasscodeBox>(session, fields));

View File

@@ -12,12 +12,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_text_entities.h"
#include "ui/boxes/confirm_box.h"
#include "data/business/data_shortcut_messages.h"
#include "data/components/scheduled_messages.h"
#include "data/data_histories.h"
#include "data/data_scheduled_messages.h"
#include "data/data_session.h"
#include "data/data_web_page.h"
#include "history/history.h"
#include "history/history_item.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "mtproto/mtproto_response.h"
@@ -29,20 +28,20 @@ namespace {
using namespace rpl::details;
template <typename T>
constexpr auto WithId =
is_callable_plain_v<T, Fn<void()>, mtpRequestId>;
constexpr auto WithId
= is_callable_plain_v<T, Fn<void()>, mtpRequestId>;
template <typename T>
constexpr auto WithoutId =
is_callable_plain_v<T, Fn<void()>>;
constexpr auto WithoutId
= is_callable_plain_v<T, Fn<void()>>;
template <typename T>
constexpr auto WithoutCallback =
is_callable_plain_v<T>;
constexpr auto WithoutCallback
= is_callable_plain_v<T>;
template <typename T>
constexpr auto ErrorWithId =
is_callable_plain_v<T, QString, mtpRequestId>;
constexpr auto ErrorWithId
= is_callable_plain_v<T, QString, mtpRequestId>;
template <typename T>
constexpr auto ErrorWithoutId =
is_callable_plain_v<T, QString>;
constexpr auto ErrorWithoutId
= is_callable_plain_v<T, QString>;
template <typename DoneCallback, typename FailCallback>
mtpRequestId EditMessage(
@@ -95,7 +94,7 @@ mtpRequestId EditMessage(
: emptyFlag);
const auto id = item->isScheduled()
? session->data().scheduledMessages().lookupId(item)
? session->scheduledMessages().lookupId(item)
: item->isBusinessShortcut()
? session->data().shortcutMessages().lookupId(item)
: item->id;

View File

@@ -34,7 +34,7 @@ namespace {
constexpr auto kSharedMediaLimit = 100;
[[nodiscard]] SendMediaReady PreparePeerPhoto(
[[nodiscard]] std::shared_ptr<FilePrepareResult> PreparePeerPhoto(
MTP::DcId dcId,
PeerId peerId,
QImage &&image) {
@@ -80,24 +80,17 @@ constexpr auto kSharedMediaLimit = 100;
MTPVector<MTPVideoSize>(),
MTP_int(dcId));
QString file, filename;
int64 filesize = 0;
QByteArray data;
return SendMediaReady(
SendMediaType::Photo,
file,
filename,
filesize,
data,
id,
id,
u"jpg"_q,
peerId,
photo,
photoThumbs,
MTP_documentEmpty(MTP_long(0)),
jpeg);
auto result = MakePreparedFile({
.id = id,
.type = SendMediaType::Photo,
});
result->type = SendMediaType::Photo;
result->setFileData(jpeg);
result->thumbId = id;
result->thumbname = "thumb.jpg";
result->photo = photo;
result->photoThumbs = photoThumbs;
return result;
}
[[nodiscard]] std::optional<MTPVideoSize> PrepareMtpMarkup(
@@ -239,7 +232,7 @@ void PeerPhoto::upload(
_api.instance().mainDcId(),
peer->id,
base::take(photo.image));
_session->uploader().uploadMedia(fakeId, ready);
_session->uploader().upload(fakeId, ready);
}
}

View File

@@ -602,6 +602,41 @@ bool PremiumGiftCodeOptions::giveawayGiftsPurchaseAvailable() const {
false);
}
SponsoredToggle::SponsoredToggle(not_null<Main::Session*> session)
: _api(&session->api().instance()) {
}
rpl::producer<bool> SponsoredToggle::toggled() {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
_api.request(MTPusers_GetFullUser(
MTP_inputUserSelf()
)).done([=](const MTPusers_UserFull &result) {
consumer.put_next_copy(
result.data().vfull_user().data().is_sponsored_enabled());
}).fail([=] { consumer.put_next(false); }).send();
return lifetime;
};
}
rpl::producer<rpl::no_value, QString> SponsoredToggle::setToggled(bool v) {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
_api.request(MTPaccount_ToggleSponsoredMessages(
MTP_bool(v)
)).done([=] {
consumer.put_done();
}).fail([=](const MTP::Error &error) {
consumer.put_error_copy(error.type());
}).send();
return lifetime;
};
}
RequirePremiumState ResolveRequiresPremiumToWrite(
not_null<PeerData*> peer,
History *maybeHistory) {

View File

@@ -216,6 +216,18 @@ private:
};
class SponsoredToggle final {
public:
explicit SponsoredToggle(not_null<Main::Session*> session);
[[nodiscard]] rpl::producer<bool> toggled();
[[nodiscard]] rpl::producer<rpl::no_value, QString> setToggled(bool);
private:
MTP::Sender _api;
};
enum class RequirePremiumState {
Unknown,
Yes,

View File

@@ -24,16 +24,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Api {
namespace {
SendMediaReady PrepareRingtoneDocument(
std::shared_ptr<FilePrepareResult> PrepareRingtoneDocument(
MTP::DcId dcId,
const QString &filename,
const QString &filemime,
const QByteArray &content) {
const auto id = base::RandomValue<DocumentId>();
auto attributes = QVector<MTPDocumentAttribute>(
1,
MTP_documentAttributeFilename(MTP_string(filename)));
const auto id = base::RandomValue<DocumentId>();
const auto document = MTP_document(
auto result = MakePreparedFile({
.id = id,
.type = SendMediaType::File,
});
result->filename = filename;
result->content = content;
result->filesize = content.size();
result->setFileData(content);
result->document = MTP_document(
MTP_flags(0),
MTP_long(id),
MTP_long(0),
@@ -45,21 +54,7 @@ SendMediaReady PrepareRingtoneDocument(
MTPVector<MTPVideoSize>(),
MTP_int(dcId),
MTP_vector<MTPDocumentAttribute>(std::move(attributes)));
return SendMediaReady(
SendMediaType::File,
QString(), // filepath
filename,
content.size(),
content,
id,
0,
QString(),
PeerId(),
MTP_photoEmpty(MTP_long(0)),
PreparedPhotoThumbs(),
document,
QByteArray());
return result;
}
} // namespace
@@ -102,7 +97,7 @@ void Ringtones::upload(
_uploads.erase(already);
}
_uploads.emplace(fakeId, uploadedData);
_session->uploader().uploadMedia(fakeId, ready);
_session->uploader().upload(fakeId, ready);
}
void Ringtones::ready(const FullMsgId &msgId, const MTPInputFile &file) {

View File

@@ -353,7 +353,7 @@ void FillMessagePostFlags(
void SendConfirmedFile(
not_null<Main::Session*> session,
const std::shared_ptr<FileLoadResult> &file) {
const std::shared_ptr<FilePrepareResult> &file) {
const auto isEditing = (file->type != SendMediaType::Audio)
&& (file->to.replaceMediaOf != 0);
const auto newId = FullMsgId(

View File

@@ -14,7 +14,7 @@ class Session;
class History;
class PhotoData;
class DocumentData;
struct FileLoadResult;
struct FilePrepareResult;
namespace Api {
@@ -40,6 +40,6 @@ void FillMessagePostFlags(
void SendConfirmedFile(
not_null<Main::Session*> session,
const std::shared_ptr<FileLoadResult> &file);
const std::shared_ptr<FilePrepareResult> &file);
} // namespace Api

View File

@@ -760,14 +760,14 @@ rpl::producer<rpl::no_value, QString> EarnStatistics::request() {
channel()->inputChannel
)).done([=](const MTPstats_BroadcastRevenueStats &result) {
const auto &data = result.data();
const auto &balances = data.vbalances().data();
_data = Data::EarnStatistics{
.topHoursGraph = StatisticalGraphFromTL(
data.vtop_hours_graph()),
.revenueGraph = StatisticalGraphFromTL(data.vrevenue_graph()),
.currentBalance = data.vcurrent_balance().v,
.availableBalance = data.vavailable_balance().v,
.overallRevenue = data.voverall_revenue().v,
.currentBalance = balances.vcurrent_balance().v,
.availableBalance = balances.vavailable_balance().v,
.overallRevenue = balances.voverall_revenue().v,
.usdRate = data.vusd_rate().v,
};
@@ -860,6 +860,7 @@ void EarnStatistics::requestHistory(
.token = Data::EarnHistorySlice::OffsetToken(nextToken),
});
}).fail([=] {
done({});
_requestId = 0;
}).send();
}

View File

@@ -21,6 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/mtproto_config.h"
#include "mtproto/mtproto_dc_options.h"
#include "data/business/data_shortcut_messages.h"
#include "data/components/scheduled_messages.h"
#include "data/components/top_peers.h"
#include "data/notify/data_notify_settings.h"
#include "data/stickers/data_stickers.h"
#include "data/data_saved_messages.h"
@@ -37,7 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_histories.h"
#include "data/data_folder.h"
#include "data/data_forum.h"
#include "data/data_scheduled_messages.h"
#include "data/data_send_action.h"
#include "data/data_stories.h"
#include "data/data_message_reactions.h"
@@ -94,7 +95,7 @@ void ProcessScheduledMessageWithElapsedTime(
// Note that when a message is scheduled until online
// while the recipient is already online, the server sends
// an ordinary new message with skipped "from_scheduled" flag.
session->data().scheduledMessages().checkEntitiesAndUpdate(data);
session->scheduledMessages().checkEntitiesAndUpdate(data);
}
}
@@ -1464,7 +1465,9 @@ void Updates::applyUpdates(
if (const auto id = owner.messageIdByRandomId(randomId)) {
const auto local = owner.message(id);
if (local && local->isScheduled()) {
owner.scheduledMessages().sendNowSimpleMessage(d, local);
session().scheduledMessages().sendNowSimpleMessage(
d,
local);
}
}
const auto wasAlready = (lookupMessage() != nullptr);
@@ -1561,7 +1564,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
auto &owner = session().data();
if (const auto local = owner.message(id)) {
if (local->isScheduled()) {
session().data().scheduledMessages().apply(d, local);
session().scheduledMessages().apply(d, local);
} else if (local->isBusinessShortcut()) {
session().data().shortcutMessages().apply(d, local);
} else {
@@ -1575,6 +1578,11 @@ void Updates::feedUpdate(const MTPUpdate &update) {
} else {
if (existing) {
existing->destroy();
} else {
// Not the server-side date, but close enough.
session().topPeers().increment(
local->history()->peer,
local->date());
}
local->setRealId(d.vid().v);
}
@@ -1771,12 +1779,12 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateNewScheduledMessage: {
const auto &d = update.c_updateNewScheduledMessage();
session().data().scheduledMessages().apply(d);
session().scheduledMessages().apply(d);
} break;
case mtpc_updateDeleteScheduledMessages: {
const auto &d = update.c_updateDeleteScheduledMessages();
session().data().scheduledMessages().apply(d);
session().scheduledMessages().apply(d);
} break;
case mtpc_updateQuickReplies: {

View File

@@ -307,8 +307,8 @@ void UserPrivacy::reload(Key key) {
}
void UserPrivacy::pushPrivacy(Key key, const TLRules &rules) {
const auto &saved = (_privacyValues[key] =
TLToRules(rules, _session->data()));
const auto &saved
= (_privacyValues[key] = TLToRules(rules, _session->data()));
const auto i = _privacyChanges.find(key);
if (i != end(_privacyChanges)) {
i->second.fire_copy(saved);

View File

@@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_user_names.h"
#include "api/api_websites.h"
#include "data/business/data_shortcut_messages.h"
#include "data/components/scheduled_messages.h"
#include "data/notify/data_notify_settings.h"
#include "data/data_changes.h"
#include "data/data_web_page.h"
@@ -43,7 +44,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_forum.h"
#include "data/data_saved_sublist.h"
#include "data/data_search_controller.h"
#include "data/data_scheduled_messages.h"
#include "data/data_session.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
@@ -542,7 +542,7 @@ void ApiWrap::sendMessageFail(
}
}
} else if (error == u"SCHEDULE_STATUS_PRIVATE"_q) {
auto &scheduled = _session->data().scheduledMessages();
auto &scheduled = _session->scheduledMessages();
Assert(peer->isUser());
if (const auto item = scheduled.lookupItem(peer->id, itemId.msg)) {
scheduled.removeSending(item);
@@ -1544,8 +1544,8 @@ void ApiWrap::saveStickerSets(
writeRecent = true;
}
const auto isAttached =
(removedSetId == Data::Stickers::CloudRecentAttachedSetId);
const auto isAttached
= (removedSetId == Data::Stickers::CloudRecentAttachedSetId);
const auto flags = isAttached
? MTPmessages_ClearRecentStickers::Flag::f_attached
: MTPmessages_ClearRecentStickers::Flags(0);
@@ -2447,8 +2447,8 @@ void ApiWrap::refreshFileReference(
_session->data().peer(storyId.peer)->input,
MTP_vector<MTPint>(1, MTP_int(storyId.story))));
} else if (item->isScheduled()) {
const auto &scheduled = _session->data().scheduledMessages();
const auto realId = scheduled.lookupId(item);
const auto realId = _session->scheduledMessages().lookupId(
item);
request(MTPmessages_GetScheduledMessages(
item->history()->peer->input,
MTP_vector<MTPint>(1, MTP_int(realId))));
@@ -2494,8 +2494,8 @@ void ApiWrap::refreshFileReference(
}, [&](Data::FileOriginPeerPhoto data) {
fail();
}, [&](Data::FileOriginStickerSet data) {
const auto isRecentAttached =
(data.setId == Data::Stickers::CloudRecentAttachedSetId);
const auto isRecentAttached
= (data.setId == Data::Stickers::CloudRecentAttachedSetId);
if (data.setId == Data::Stickers::CloudRecentSetId
|| data.setId == Data::Stickers::RecentSetId
|| isRecentAttached) {

View File

@@ -605,8 +605,8 @@ void GroupInfoBox::prepare() {
_navigation->session().api().selfDestruct().reload();
const auto top = addTopButton(st::infoTopBarMenu);
const auto menu =
top->lifetime().make_state<base::unique_qptr<Ui::PopupMenu>>();
const auto menu
= top->lifetime().make_state<base::unique_qptr<Ui::PopupMenu>>();
top->setClickedCallback([=] {
*menu = base::make_unique_q<Ui::PopupMenu>(
top,
@@ -1306,8 +1306,8 @@ void SetupChannelBox::handleChange() {
&& (ch < 'a' || ch > 'z')
&& (ch < '0' || ch > '9')
&& ch != '_') {
const auto badSymbols =
tr::lng_create_channel_link_bad_symbols(tr::now);
const auto badSymbols
= tr::lng_create_channel_link_bad_symbols(tr::now);
if (_errorText != badSymbols) {
_errorText = badSymbols;
update();
@@ -1317,8 +1317,8 @@ void SetupChannelBox::handleChange() {
}
}
if (name.size() < Ui::EditPeer::kMinUsernameLength) {
const auto tooShort =
tr::lng_create_channel_link_too_short(tr::now);
const auto tooShort
= tr::lng_create_channel_link_too_short(tr::now);
if (_errorText != tooShort) {
_errorText = tooShort;
update();

View File

@@ -593,11 +593,11 @@ void BackgroundPreviewBox::uploadForPeer(bool both) {
const auto ready = Window::Theme::PrepareWallPaper(
session->mainDcId(),
_paper.localThumbnail()->original());
const auto documentId = ready.id;
const auto documentId = ready->id;
_uploadId = FullMsgId(
session->userPeerId(),
session->data().nextLocalMessageId());
session->uploader().uploadMedia(_uploadId, ready);
session->uploader().upload(_uploadId, ready);
if (_uploadLifetime) {
return;
}
@@ -940,7 +940,7 @@ int BackgroundPreviewBox::textsTop() const {
- st::historyPaddingBottom
- (_service ? _service->height() : 0)
- _text1->height()
- (forChannel() ? _text2->height() : 0);
- (forChannel() ? 0 : _text2->height());
}
QRect BackgroundPreviewBox::radialRect() const {

View File

@@ -546,7 +546,7 @@ rightsToggle: Toggle(defaultToggle) {
vsize: 5px;
vshift: 1px;
stroke: 2px;
duration: 120;
duration: universalDuration;
}
rightsButton: SettingsButton(defaultSettingsButton) {
@@ -691,6 +691,10 @@ createPollOptionField: InputField(createPollField) {
placeholderMargins: margins(2px, 0px, 2px, 0px);
heightMax: 68px;
}
createPollOptionFieldPremium: InputField(createPollOptionField) {
textMargins: margins(22px, 11px, 68px, 11px);
}
createPollOptionFieldPremiumEmojiPosition: point(15px, -1px);
createPollSolutionField: InputField(createPollField) {
textMargins: margins(0px, 4px, 0px, 4px);
border: 1px;
@@ -704,7 +708,7 @@ createPollOptionRemove: CrossButton {
cross: CrossAnimation {
size: 22px;
skip: 6px;
stroke: 1.;
stroke: 1.5;
minScale: 0.3;
}
crossFg: boxTitleCloseFg;
@@ -718,6 +722,7 @@ createPollOptionRemove: CrossButton {
}
}
createPollOptionRemovePosition: point(11px, 9px);
createPollOptionEmojiPositionSkip: 4px;
createPollWarning: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg;
palette: TextPalette(defaultTextPalette) {
@@ -1074,3 +1079,23 @@ collectibleBox: Box(defaultBox) {
buttonHeight: 36px;
button: collectibleCopy;
}
moderateBoxUserpic: UserpicButton(defaultUserpicButton) {
size: size(34px, 42px);
photoSize: 34px;
photoPosition: point(0px, 4px);
}
moderateBoxExpand: icon {{ "chat/reply_type_group", boxTextFg }};
moderateBoxExpandHeight: 20px;
moderateBoxExpandRight: 10px;
moderateBoxExpandInnerSkip: 2px;
moderateBoxExpandFont: font(11px);
moderateBoxExpandToggleSize: 4px;
moderateBoxExpandToggleFourStrokes: 3px;
moderateBoxExpandIcon: icon{{ "info/edit/expand_arrow_small-flip_vertical", windowActiveTextFg }};
moderateBoxExpandIconDown: icon{{ "info/edit/expand_arrow_small", windowActiveTextFg }};
moderateBoxDividerLabel: FlatLabel(boxDividerLabel) {
palette: TextPalette(defaultTextPalette) {
selectLinkFg: windowActiveTextFg;
}
}

View File

@@ -7,33 +7,41 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/create_poll_box.h"
#include "lang/lang_keys.h"
#include "data/data_poll.h"
#include "ui/toast/toast.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/text/text_utilities.h"
#include "ui/vertical_list.h"
#include "main/main_session.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "base/call_delayed.h"
#include "base/event_filter.h"
#include "base/random.h"
#include "base/unique_qptr.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h"
#include "menu/menu_send.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "data/data_poll.h"
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h"
#include "history/view/history_view_schedule_box.h"
#include "base/unique_qptr.h"
#include "base/event_filter.h"
#include "base/call_delayed.h"
#include "base/random.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "menu/menu_send.h"
#include "ui/controls/emoji_button.h"
#include "ui/rect.h"
#include "ui/text/text_utilities.h"
#include "ui/toast/toast.h"
#include "ui/vertical_list.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/shadow.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "window/window_session_controller.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h" // defaultComposeFiles.
#include "styles/style_layers.h"
#include "styles/style_settings.h"
namespace {
@@ -46,12 +54,107 @@ constexpr auto kSolutionLimit = 200;
constexpr auto kWarnSolutionLimit = 60;
constexpr auto kErrorLimit = 99;
[[nodiscard]] not_null<Ui::EmojiButton*> AddEmojiToggleToField(
not_null<Ui::InputField*> field,
not_null<Ui::BoxContent*> box,
not_null<Window::SessionController*> controller,
not_null<ChatHelpers::TabbedPanel*> emojiPanel,
QPoint shift) {
const auto emojiToggle = Ui::CreateChild<Ui::EmojiButton>(
field->parentWidget(),
st::defaultComposeFiles.emoji);
const auto fade = Ui::CreateChild<Ui::FadeAnimation>(
emojiToggle,
emojiToggle,
0.5);
{
const auto fadeTarget = Ui::CreateChild<Ui::RpWidget>(emojiToggle);
fadeTarget->resize(emojiToggle->size());
fadeTarget->paintRequest(
) | rpl::start_with_next([=](const QRect &rect) {
auto p = QPainter(fadeTarget);
if (fade->animating()) {
p.fillRect(fadeTarget->rect(), st::boxBg);
}
fade->paint(p);
}, fadeTarget->lifetime());
rpl::single(false) | rpl::then(
field->focusedChanges()
) | rpl::start_with_next([=](bool shown) {
if (shown) {
fade->fadeIn(st::universalDuration);
} else {
fade->fadeOut(st::universalDuration);
}
}, emojiToggle->lifetime());
fade->fadeOut(1);
fade->finish();
}
const auto outer = box->getDelegate()->outerContainer();
const auto allow = [](not_null<DocumentData*>) { return true; };
InitMessageFieldHandlers(
controller,
field,
Window::GifPauseReason::Layer,
allow);
Ui::Emoji::SuggestionsController::Init(
outer,
field,
&controller->session(),
Ui::Emoji::SuggestionsController::Options{
.suggestCustomEmoji = true,
.allowCustomWithoutPremium = allow,
});
const auto updateEmojiPanelGeometry = [=] {
const auto parent = emojiPanel->parentWidget();
const auto global = emojiToggle->mapToGlobal({ 0, 0 });
const auto local = parent->mapFromGlobal(global);
const auto right = local.x() + emojiToggle->width() * 3;
const auto isDropDown = local.y() < parent->height() / 2;
emojiPanel->setDropDown(isDropDown);
if (isDropDown) {
emojiPanel->moveTopRight(
local.y() + emojiToggle->height(),
right);
} else {
emojiPanel->moveBottomRight(local.y(), right);
}
};
rpl::combine(
box->sizeValue(),
field->geometryValue()
) | rpl::start_with_next([=](QSize outer, QRect inner) {
emojiToggle->moveToLeft(
rect::right(inner) + shift.x(),
inner.y() + shift.y());
emojiToggle->update();
}, emojiToggle->lifetime());
emojiToggle->installEventFilter(emojiPanel);
emojiToggle->addClickHandler([=] {
updateEmojiPanelGeometry();
emojiPanel->toggleAnimated();
});
const auto filterCallback = [=](not_null<QEvent*> event) {
if (event->type() == QEvent::Enter) {
updateEmojiPanelGeometry();
}
return base::EventFilterResult::Continue;
};
base::install_event_filter(emojiToggle, filterCallback);
return emojiToggle;
}
class Options {
public:
Options(
not_null<QWidget*> outer,
not_null<Ui::BoxContent*> box,
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session,
not_null<Window::SessionController*> controller,
ChatHelpers::TabbedPanel *emojiPanel,
bool chooseCorrectEnabled);
[[nodiscard]] bool hasOptions() const;
@@ -140,9 +243,10 @@ private:
[[nodiscard]] auto createChooseCorrectGroup()
-> std::shared_ptr<Ui::RadiobuttonGroup>;
not_null<QWidget*> _outer;
not_null<Ui::BoxContent*> _box;
not_null<Ui::VerticalLayout*> _container;
const not_null<Main::Session*> _session;
const not_null<Window::SessionController*> _controller;
ChatHelpers::TabbedPanel * const _emojiPanel;
std::shared_ptr<Ui::RadiobuttonGroup> _chooseCorrectGroup;
int _position = 0;
std::vector<std::unique_ptr<Option>> _list;
@@ -154,6 +258,7 @@ private:
rpl::event_stream<not_null<QWidget*>> _scrollToWidget;
rpl::event_stream<> _backspaceInFront;
rpl::event_stream<> _tabbed;
rpl::lifetime _emojiPanelLifetime;
};
@@ -193,8 +298,9 @@ not_null<Ui::FlatLabel*> CreateWarningLabel(
if (value >= 0) {
result->setText(QString::number(value));
} else {
constexpr auto kMinus = QChar(0x2212);
result->setMarkedText(Ui::Text::Colorized(
QString::number(value)));
kMinus + QString::number(std::abs(value))));
}
result->setVisible(shown);
}));
@@ -223,7 +329,9 @@ Options::Option::Option(
, _field(
Ui::CreateChild<Ui::InputField>(
_content.get(),
st::createPollOptionField,
session->user()->isPremium()
? st::createPollOptionFieldPremium
: st::createPollOptionField,
Ui::InputField::Mode::NoNewlines,
tr::lng_polls_create_option_add())) {
InitField(outer, _field, session);
@@ -299,7 +407,7 @@ void Options::Option::createRemove() {
const auto remove = Ui::CreateChild<Ui::CrossButton>(
field.get(),
st::createPollOptionRemove);
remove->hide(anim::type::instant);
remove->show(anim::type::instant);
const auto toggle = lifetime.make_state<rpl::variable<bool>>(false);
_removeAlways = lifetime.make_state<rpl::variable<bool>>(false);
@@ -309,6 +417,7 @@ void Options::Option::createRemove() {
// Don't capture 'this'! Because Option is a value type.
*toggle = !field->getLastText().isEmpty();
}, field->lifetime());
#if 0
rpl::combine(
toggle->value(),
_removeAlways->value(),
@@ -316,6 +425,7 @@ void Options::Option::createRemove() {
) | rpl::start_with_next([=](bool shown) {
remove->toggle(shown, anim::type::normal);
}, remove->lifetime());
#endif
field->widthValue(
) | rpl::start_with_next([=](int width) {
@@ -456,10 +566,16 @@ void Options::Option::removePlaceholder() const {
PollAnswer Options::Option::toPollAnswer(int index) const {
Expects(index >= 0 && index < kMaxOptionsCount);
const auto text = field()->getTextWithTags();
auto result = PollAnswer{
field()->getLastText().trimmed(),
QByteArray(1, ('0' + index))
TextWithEntities{
.text = text.text,
.entities = TextUtilities::ConvertTextTagsToEntities(text.tags),
},
QByteArray(1, ('0' + index)),
};
TextUtilities::Trim(result.text);
result.correct = _correct ? _correct->entity()->Checkbox::checked() : false;
return result;
}
@@ -469,13 +585,15 @@ rpl::producer<Qt::MouseButton> Options::Option::removeClicks() const {
}
Options::Options(
not_null<QWidget*> outer,
not_null<Ui::BoxContent*> box,
not_null<Ui::VerticalLayout*> container,
not_null<Main::Session*> session,
not_null<Window::SessionController*> controller,
ChatHelpers::TabbedPanel *emojiPanel,
bool chooseCorrectEnabled)
: _outer(outer)
: _box(box)
, _container(container)
, _session(session)
, _controller(controller)
, _emojiPanel(emojiPanel)
, _chooseCorrectGroup(chooseCorrectEnabled
? createChooseCorrectGroup()
: nullptr)
@@ -645,12 +763,40 @@ void Options::addEmptyOption() {
(*(_list.end() - 2))->toggleRemoveAlways(true);
}
_list.push_back(std::make_unique<Option>(
_outer,
_box,
_container,
_session,
&_controller->session(),
_position + _list.size() + _destroyed.size(),
_chooseCorrectGroup));
const auto field = _list.back()->field();
if (const auto emojiPanel = _emojiPanel) {
const auto emojiToggle = AddEmojiToggleToField(
field,
_box,
_controller,
emojiPanel,
QPoint(
-st::createPollOptionFieldPremium.textMargins.right(),
st::createPollOptionEmojiPositionSkip));
emojiToggle->shownValue() | rpl::start_with_next([=](bool shown) {
if (!shown) {
return;
}
_emojiPanelLifetime.destroy();
emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
if (field->hasFocus()) {
Ui::InsertEmojiAtCursor(field->textCursor(), data.emoji);
}
}, _emojiPanelLifetime);
emojiPanel->selector()->customEmojiChosen(
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
if (field->hasFocus()) {
Data::InsertCustomEmoji(field, data.document);
}
}, _emojiPanelLifetime);
}, emojiToggle->lifetime());
}
field->submits(
) | rpl::start_with_next([=] {
const auto index = findField(field);
@@ -697,7 +843,7 @@ void Options::addEmptyOption() {
});
_list.back()->removeClicks(
) | rpl::take(1) | rpl::start_with_next([=] {
) | rpl::start_with_next([=] {
Ui::PostponeCall(crl::guard(field, [=] {
Expects(!_list.empty());
@@ -789,19 +935,63 @@ not_null<Ui::InputField*> CreatePollBox::setupQuestion(
using namespace Settings;
const auto session = &_controller->session();
const auto isPremium = session->user()->isPremium();
Ui::AddSubsectionTitle(container, tr::lng_polls_create_question());
const auto question = container->add(
object_ptr<Ui::InputField>(
container,
st::createPollField,
Ui::InputField::Mode::MultiLine,
tr::lng_polls_create_question_placeholder()),
st::createPollFieldPadding);
st::createPollFieldPadding
+ (isPremium
? QMargins(0, 0, st::defaultComposeFiles.emoji.inner.width, 0)
: QMargins()));
InitField(getDelegate()->outerContainer(), question, session);
question->setMaxLength(kQuestionLimit + kErrorLimit);
question->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
question->customTab(true);
if (isPremium) {
using Selector = ChatHelpers::TabbedSelector;
const auto outer = getDelegate()->outerContainer();
_emojiPanel = base::make_unique_q<ChatHelpers::TabbedPanel>(
outer,
_controller,
object_ptr<Selector>(
nullptr,
_controller->uiShow(),
Window::GifPauseReason::Layer,
Selector::Mode::EmojiOnly));
const auto emojiPanel = _emojiPanel.get();
emojiPanel->setDesiredHeightValues(
1.,
st::emojiPanMinHeight / 2,
st::emojiPanMinHeight);
emojiPanel->hide();
emojiPanel->selector()->setCurrentPeer(session->user());
const auto emojiToggle = AddEmojiToggleToField(
question,
this,
_controller,
emojiPanel,
st::createPollOptionFieldPremiumEmojiPosition);
emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
if (question->hasFocus()) {
Ui::InsertEmojiAtCursor(question->textCursor(), data.emoji);
}
}, emojiToggle->lifetime());
emojiPanel->selector()->customEmojiChosen(
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
if (question->hasFocus()) {
Data::InsertCustomEmoji(question, data.document);
}
}, emojiToggle->lifetime());
}
const auto warning = CreateWarningLabel(
container,
question,
@@ -910,9 +1100,10 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
st::defaultSubsectionTitle),
st::createPollFieldTitlePadding);
const auto options = lifetime().make_state<Options>(
getDelegate()->outerContainer(),
this,
container,
&_controller->session(),
_controller,
_emojiPanel ? _emojiPanel.get() : nullptr,
(_chosen & PollData::Flag::Quiz));
auto limit = options->usedCount() | rpl::after_next([=](int count) {
setCloseByEscape(!count);
@@ -1029,9 +1220,13 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
};
const auto collectResult = [=] {
const auto textWithTags = question->getTextWithTags();
using Flag = PollData::Flag;
auto result = PollData(&_controller->session().data(), id);
result.question = question->getLastText().trimmed();
result.question.text = textWithTags.text;
result.question.entities = TextUtilities::ConvertTextTagsToEntities(
textWithTags.tags);
TextUtilities::Trim(result.question);
result.answers = options->toPollAnswers();
const auto solutionWithTags = quiz->checked()
? solution->getTextWithAppliedMarkdown()

View File

@@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
struct PollData;
namespace ChatHelpers {
class TabbedPanel;
} // namespace ChatHelpers
namespace Ui {
class VerticalLayout;
} // namespace Ui
@@ -72,6 +76,7 @@ private:
const PollData::Flags _disabled = PollData::Flags();
const Api::SendType _sendType = Api::SendType();
const SendMenu::Type _sendMenuType;
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
Fn<void()> _setInnerFocus;
Fn<rpl::producer<bool>()> _dataIsValidValue;
rpl::event_stream<Result> _submitRequests;

View File

@@ -232,8 +232,8 @@ void DeleteMessagesBox::prepare() {
if (hasScheduledMessages()) {
} else if (auto revoke = revokeText(peer)) {
const auto &settings = Core::App().settings();
const auto revokeByDefault =
!settings.rememberedDeleteMessageOnlyForYou();
const auto revokeByDefault
= !settings.rememberedDeleteMessageOnlyForYou();
_revoke.create(
this,
revoke->checkbox,
@@ -379,13 +379,7 @@ auto DeleteMessagesBox::revokeText(not_null<PeerData*> peer) const
return result;
}
const auto items = ranges::views::all(
_ids
) | ranges::views::transform([&](FullMsgId id) {
return peer->owner().message(id);
}) | ranges::views::filter([](HistoryItem *item) {
return (item != nullptr);
}) | ranges::to_vector;
const auto items = peer->owner().idsToItems(_ids);
if (items.size() != _ids.size()) {
// We don't have information about all messages.

View File

@@ -159,10 +159,7 @@ public:
-> rpl::producer<RowSelectionChange>;
private:
[[nodiscard]] std::unique_ptr<PeerListRow> createRow() const;
const not_null<Main::Session*> _session;
bool _premiums = false;
rpl::event_stream<> _selectionChanged;
rpl::event_stream<RowSelectionChange> _rowSelectionChanges;
@@ -209,8 +206,7 @@ bool PremiumsRow::useForumLikeUserpic() const {
TypesController::TypesController(
not_null<Main::Session*> session,
bool premiums)
: _session(session)
, _premiums(premiums) {
: _session(session) {
}
Main::Session &TypesController::session() const {
@@ -331,6 +327,9 @@ auto PrivacyExceptionsBoxController::preparePremiumsRowList()
_deselectOption = [=](PeerListRowId itemId) {
if (const auto row = _typesDelegate->peerListFindRow(itemId)) {
if (itemId == kPremiumsRowId) {
_selected.premiums = false;
}
_typesDelegate->peerListSetRowChecked(row, false);
}
};

View File

@@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/prepare_short_info_box.h"
#include "boxes/peers/replace_boost_box.h" // BoostsForGift.
#include "boxes/premium_preview_box.h" // ShowPremiumPreviewBox.
#include "core/ui_integration.h" // Core::MarkedTextContext.
#include "data/data_boosts.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
@@ -48,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/gradient_round_button.h"
#include "ui/widgets/label_with_custom_emoji.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/table_layout.h"
@@ -319,21 +319,20 @@ void GiftBox(
std::move(titleLabel)),
st::premiumGiftTitlePadding);
auto textLabel = object_ptr<Ui::FlatLabel>(box, st::premiumPreviewAbout);
tr::lng_premium_gift_about(
lt_user,
user->session().changes().peerFlagsValue(
user,
Data::PeerUpdate::Flag::Name
) | rpl::map([=] { return TextWithEntities{ user->firstName }; }),
Ui::Text::RichLangValue
) | rpl::map(
BoostsForGiftText({ user })
) | rpl::start_with_next([
raw = textLabel.data(),
session = &user->session()](const TextWithEntities &t) {
raw->setMarkedText(t, Core::MarkedTextContext{ .session = session });
}, textLabel->lifetime());
auto textLabel = Ui::CreateLabelWithCustomEmoji(
box,
tr::lng_premium_gift_about(
lt_user,
user->session().changes().peerFlagsValue(
user,
Data::PeerUpdate::Flag::Name
) | rpl::map([=] { return TextWithEntities{ user->firstName }; }),
Ui::Text::RichLangValue
) | rpl::map(
BoostsForGiftText({ user })
),
{ .session = &user->session() },
st::premiumPreviewAbout);
textLabel->setTextColorOverride(stTitle.textFg->c);
textLabel->resizeToWidth(available);
box->addRow(
@@ -536,14 +535,12 @@ void GiftsBox(
const auto label = box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
box,
object_ptr<Ui::FlatLabel>(box, st::premiumPreviewAbout)),
Ui::CreateLabelWithCustomEmoji(
box,
std::move(text),
{ .session = session },
st::premiumPreviewAbout)),
padding)->entity();
std::move(
text
) | rpl::start_with_next([=](const TextWithEntities &t) {
using namespace Core;
label->setMarkedText(t, MarkedTextContext{ .session = session });
}, label->lifetime());
label->setTextColorOverride(stTitle.textFg->c);
label->resizeToWidth(available);
}

View File

@@ -0,0 +1,848 @@
/*
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 "boxes/moderate_messages_box.h"
#include "api/api_chat_participants.h"
#include "api/api_messages_search.h"
#include "apiwrap.h"
#include "base/event_filter.h"
#include "base/timer.h"
#include "boxes/delete_messages_box.h"
#include "boxes/peers/edit_peer_permissions_box.h"
#include "core/application.h"
#include "core/ui_integration.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_chat_participant_status.h"
#include "data/data_histories.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h"
#include "history/history.h"
#include "history/history_item.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/boxes/confirm_box.h"
#include "ui/controls/userpic_button.h"
#include "ui/effects/ripple_animation.h"
#include "ui/effects/toggle_arrow.h"
#include "ui/layers/generic_box.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/rect_part.h"
#include "ui/text/text_utilities.h"
#include "ui/vertical_list.h"
#include "ui/widgets/checkbox.h"
#include "ui/wrap/slide_wrap.h"
#include "styles/style_boxes.h"
#include "styles/style_layers.h"
#include "styles/style_window.h"
namespace {
using Participants = std::vector<not_null<PeerData*>>;
struct ModerateOptions final {
bool allCanBan = false;
bool allCanDelete = false;
Participants participants;
};
ModerateOptions CalculateModerateOptions(const HistoryItemsList &items) {
Expects(!items.empty());
auto result = ModerateOptions{
.allCanBan = true,
.allCanDelete = true,
};
const auto peer = items.front()->history()->peer;
for (const auto &item : items) {
if (!result.allCanBan && !result.allCanDelete) {
return {};
}
if (peer != item->history()->peer) {
return {};
}
if (!item->suggestBanReport()) {
result.allCanBan = false;
}
if (!item->suggestDeleteAllReport()) {
result.allCanDelete = false;
}
if (const auto p = item->from()) {
if (!ranges::contains(result.participants, not_null{ p })) {
result.participants.push_back(p);
}
}
}
return result;
}
[[nodiscard]] rpl::producer<int> MessagesCountValue(
not_null<History*> history,
not_null<PeerData*> from) {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
auto search = lifetime.make_state<Api::MessagesSearch>(history);
consumer.put_next(0);
search->messagesFounds(
) | rpl::start_with_next([=](const Api::FoundMessages &found) {
consumer.put_next_copy(found.total);
}, lifetime);
search->searchMessages({ .from = from });
return lifetime;
};
}
class Button final : public Ui::RippleButton {
public:
Button(not_null<QWidget*> parent, int count);
void setChecked(bool checked);
[[nodiscard]] bool checked() const;
[[nodiscard]] static QSize ComputeSize(int);
private:
void paintEvent(QPaintEvent *event) override;
QImage prepareRippleMask() const override;
QPoint prepareRippleStartPosition() const override;
const int _count;
const QString _text;
bool _checked = false;
Ui::Animations::Simple _animation;
};
Button::Button(not_null<QWidget*> parent, int count)
: RippleButton(parent, st::defaultRippleAnimation)
, _count(count)
, _text(QString::number(std::abs(_count))) {
}
QSize Button::ComputeSize(int count) {
return QSize(
st::moderateBoxExpandHeight
+ st::moderateBoxExpand.width()
+ st::moderateBoxExpandInnerSkip * 4
+ st::moderateBoxExpandFont->width(
QString::number(std::abs(count)))
+ st::moderateBoxExpandToggleSize,
st::moderateBoxExpandHeight);
}
void Button::setChecked(bool checked) {
if (_checked == checked) {
return;
}
_checked = checked;
_animation.stop();
_animation.start(
[=] { update(); },
checked ? 0 : 1,
checked ? 1 : 0,
st::slideWrapDuration);
}
bool Button::checked() const {
return _checked;
}
void Button::paintEvent(QPaintEvent *event) {
auto p = Painter(this);
auto hq = PainterHighQualityEnabler(p);
Ui::RippleButton::paintRipple(p, QPoint());
const auto radius = height() / 2;
p.setPen(Qt::NoPen);
st::moderateBoxExpand.paint(
p,
radius,
(height() - st::moderateBoxExpand.height()) / 2,
width());
const auto innerSkip = st::moderateBoxExpandInnerSkip;
p.setBrush(Qt::NoBrush);
p.setPen(st::boxTextFg);
p.setFont(st::moderateBoxExpandFont);
p.drawText(
QRect(
innerSkip + radius + st::moderateBoxExpand.width(),
0,
width(),
height()),
_text,
style::al_left);
const auto path = Ui::ToggleUpDownArrowPath(
width() - st::moderateBoxExpandToggleSize - radius,
height() / 2,
st::moderateBoxExpandToggleSize,
st::moderateBoxExpandToggleFourStrokes,
_animation.value(_checked ? 1. : 0.));
p.fillPath(path, st::boxTextFg);
}
QImage Button::prepareRippleMask() const {
return Ui::RippleAnimation::RoundRectMask(size(), size().height() / 2);
}
QPoint Button::prepareRippleStartPosition() const {
return mapFromGlobal(QCursor::pos());
}
} // namespace
void CreateModerateMessagesBox(
not_null<Ui::GenericBox*> box,
const HistoryItemsList &items,
Fn<void()> confirmed) {
struct Controller final {
rpl::event_stream<bool> toggleRequestsFromTop;
rpl::event_stream<bool> toggleRequestsFromInner;
rpl::event_stream<bool> checkAllRequests;
Fn<Participants()> collectRequests;
};
const auto [allCanBan, allCanDelete, participants]
= CalculateModerateOptions(items);
const auto inner = box->verticalLayout();
Assert(!participants.empty());
const auto confirms = inner->lifetime().make_state<rpl::event_stream<>>();
const auto isSingle = participants.size() == 1;
const auto buttonPadding = isSingle
? QMargins()
: QMargins(0, 0, Button::ComputeSize(participants.size()).width(), 0);
const auto session = &items.front()->history()->session();
const auto historyPeerId = items.front()->history()->peer->id;
using Request = Fn<void(not_null<PeerData*>, not_null<ChannelData*>)>;
const auto sequentiallyRequest = [=](
Request request,
Participants participants) {
constexpr auto kSmallDelayMs = 5;
const auto participantIds = ranges::views::all(
participants
) | ranges::views::transform([](not_null<PeerData*> peer) {
return peer->id;
}) | ranges::to_vector;
const auto lifetime = std::make_shared<rpl::lifetime>();
const auto counter = lifetime->make_state<int>(0);
const auto timer = lifetime->make_state<base::Timer>();
timer->setCallback(crl::guard(session, [=] {
if ((*counter) < participantIds.size()) {
const auto peer = session->data().peer(historyPeerId);
const auto channel = peer ? peer->asChannel() : nullptr;
const auto from = session->data().peer(
participantIds[*counter]);
if (channel && from) {
request(from, channel);
}
(*counter)++;
} else {
lifetime->destroy();
}
}));
timer->callEach(kSmallDelayMs);
};
const auto handleConfirmation = [=](
not_null<Ui::Checkbox*> checkbox,
not_null<Controller*> controller,
Request request) {
confirms->events() | rpl::start_with_next([=] {
if (checkbox->checked() && controller->collectRequests) {
sequentiallyRequest(request, controller->collectRequests());
}
}, checkbox->lifetime());
};
const auto isEnter = [=](not_null<QEvent*> event) {
if (event->type() == QEvent::KeyPress) {
if (const auto k = static_cast<QKeyEvent*>(event.get())) {
return (k->key() == Qt::Key_Enter)
|| (k->key() == Qt::Key_Return);
}
}
return false;
};
base::install_event_filter(box, [=](not_null<QEvent*> event) {
if (isEnter(event)) {
box->triggerButton(0);
return base::EventFilterResult::Cancel;
}
return base::EventFilterResult::Continue;
});
const auto handleSubmition = [=](not_null<Ui::Checkbox*> checkbox) {
base::install_event_filter(box, [=](not_null<QEvent*> event) {
if (!isEnter(event) || !checkbox->checked()) {
return base::EventFilterResult::Continue;
}
box->uiShow()->show(Ui::MakeConfirmBox({
.text = tr::lng_gigagroup_warning_title(),
.confirmed = [=](Fn<void()> close) {
box->triggerButton(0);
close();
},
.confirmText = tr::lng_box_yes(),
.cancelText = tr::lng_box_no(),
}));
return base::EventFilterResult::Cancel;
});
};
const auto createParticipantsList = [&](
not_null<Controller*> controller) {
const auto wrap = inner->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
inner,
object_ptr<Ui::VerticalLayout>(inner)));
wrap->toggle(false, anim::type::instant);
controller->toggleRequestsFromTop.events(
) | rpl::start_with_next([=](bool toggled) {
wrap->toggle(toggled, anim::type::normal);
}, wrap->lifetime());
const auto container = wrap->entity();
Ui::AddSkip(container);
auto &lifetime = wrap->lifetime();
const auto clicks = lifetime.make_state<rpl::event_stream<>>();
const auto checkboxes = ranges::views::all(
participants
) | ranges::views::transform([&](not_null<PeerData*> peer) {
const auto line = container->add(
object_ptr<Ui::AbstractButton>(container));
const auto &st = st::moderateBoxUserpic;
line->resize(line->width(), st.size.height());
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
line,
peer,
st);
const auto checkbox = Ui::CreateChild<Ui::Checkbox>(
line,
peer->name(),
false,
st::defaultBoxCheckbox);
line->widthValue(
) | rpl::start_with_next([=](int width) {
userpic->moveToLeft(
st::boxRowPadding.left()
+ checkbox->checkRect().width()
+ st::defaultBoxCheckbox.textPosition.x(),
0);
const auto skip = st::defaultBoxCheckbox.textPosition.x();
checkbox->resizeToWidth(width
- rect::right(userpic)
- skip
- st::boxRowPadding.right());
checkbox->moveToLeft(
rect::right(userpic) + skip,
((userpic->height() - checkbox->height()) / 2)
+ st::defaultBoxCheckbox.margin.top());
}, checkbox->lifetime());
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
checkbox->setAttribute(Qt::WA_TransparentForMouseEvents);
line->setClickedCallback([=] {
checkbox->setChecked(!checkbox->checked());
clicks->fire({});
});
return checkbox;
}) | ranges::to_vector;
clicks->events(
) | rpl::start_with_next([=] {
controller->toggleRequestsFromInner.fire_copy(
ranges::any_of(checkboxes, &Ui::Checkbox::checked));
}, container->lifetime());
controller->checkAllRequests.events(
) | rpl::start_with_next([=](bool checked) {
for (const auto &c : checkboxes) {
c->setChecked(checked);
}
}, container->lifetime());
controller->collectRequests = [=] {
auto result = Participants();
for (auto i = 0; i < checkboxes.size(); i++) {
if (checkboxes[i]->checked()) {
result.push_back(participants[i]);
}
}
return result;
};
};
const auto appendList = [&](
not_null<Ui::Checkbox*> checkbox,
not_null<Controller*> controller) {
if (isSingle) {
const auto p = participants.front();
controller->collectRequests = [=] { return Participants{ p }; };
return;
}
const auto count = int(participants.size());
const auto button = Ui::CreateChild<Button>(inner, count);
button->resize(Button::ComputeSize(count));
const auto overlay = Ui::CreateChild<Ui::AbstractButton>(inner);
checkbox->geometryValue(
) | rpl::start_with_next([=](const QRect &rect) {
overlay->setGeometry(rect);
overlay->raise();
button->moveToRight(
st::moderateBoxExpandRight,
rect.top() + (rect.height() - button->height()) / 2,
box->width());
button->raise();
}, button->lifetime());
controller->toggleRequestsFromInner.events(
) | rpl::start_with_next([=](bool toggled) {
checkbox->setChecked(toggled);
}, checkbox->lifetime());
button->setClickedCallback([=] {
button->setChecked(!button->checked());
controller->toggleRequestsFromTop.fire_copy(button->checked());
});
overlay->setClickedCallback([=] {
checkbox->setChecked(!checkbox->checked());
controller->checkAllRequests.fire_copy(checkbox->checked());
});
createParticipantsList(controller);
};
Ui::AddSkip(inner);
const auto title = box->addRow(
object_ptr<Ui::FlatLabel>(
box,
(items.size() == 1)
? tr::lng_selected_delete_sure_this()
: tr::lng_selected_delete_sure(
lt_count,
rpl::single(items.size()) | tr::to_count()),
st::boxLabel));
Ui::AddSkip(inner);
Ui::AddSkip(inner);
Ui::AddSkip(inner);
{
const auto report = box->addRow(
object_ptr<Ui::Checkbox>(
box,
tr::lng_report_spam(tr::now),
false,
st::defaultBoxCheckbox),
st::boxRowPadding + buttonPadding);
const auto controller = box->lifetime().make_state<Controller>();
appendList(report, controller);
handleSubmition(report);
const auto ids = items.front()->from()->owner().itemsToIds(items);
handleConfirmation(report, controller, [=](
not_null<PeerData*> p,
not_null<ChannelData*> c) {
auto filtered = ranges::views::all(
ids
) | ranges::views::transform([](const FullMsgId &id) {
return MTP_int(id.msg);
}) | ranges::to<QVector<MTPint>>();
c->session().api().request(
MTPchannels_ReportSpam(
c->inputChannel,
p->input,
MTP_vector<MTPint>(std::move(filtered)))
).send();
});
}
if (allCanDelete) {
Ui::AddSkip(inner);
Ui::AddSkip(inner);
const auto deleteAll = inner->add(
object_ptr<Ui::Checkbox>(
inner,
!(isSingle)
? tr::lng_delete_all_from_users(
tr::now,
Ui::Text::WithEntities)
: tr::lng_delete_all_from_user(
tr::now,
lt_user,
Ui::Text::Bold(items.front()->from()->name()),
Ui::Text::WithEntities),
false,
st::defaultBoxCheckbox),
st::boxRowPadding + buttonPadding);
if (isSingle) {
const auto history = items.front()->history();
tr::lng_selected_delete_sure(
lt_count,
rpl::combine(
MessagesCountValue(history, participants.front()),
deleteAll->checkedValue()
) | rpl::map([s = items.size()](int all, bool checked) {
return float64((checked && all) ? all : s);
})
) | rpl::start_with_next([=](const QString &text) {
title->setText(text);
title->resizeToWidth(inner->width()
- rect::m::sum::h(st::boxRowPadding));
}, title->lifetime());
}
const auto controller = box->lifetime().make_state<Controller>();
appendList(deleteAll, controller);
handleSubmition(deleteAll);
handleConfirmation(deleteAll, controller, [=](
not_null<PeerData*> p,
not_null<ChannelData*> c) {
p->session().api().deleteAllFromParticipant(c, p);
});
}
if (allCanBan) {
auto ownedWrap = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
inner,
object_ptr<Ui::VerticalLayout>(inner));
Ui::AddSkip(inner);
Ui::AddSkip(inner);
const auto ban = inner->add(
object_ptr<Ui::Checkbox>(
box,
rpl::conditional(
ownedWrap->toggledValue(),
tr::lng_context_restrict_user(),
rpl::conditional(
rpl::single(isSingle),
tr::lng_ban_user(),
tr::lng_ban_users())),
false,
st::defaultBoxCheckbox),
st::boxRowPadding + buttonPadding);
const auto controller = box->lifetime().make_state<Controller>();
appendList(ban, controller);
handleSubmition(ban);
Ui::AddSkip(inner);
Ui::AddSkip(inner);
const auto wrap = inner->add(std::move(ownedWrap));
const auto container = wrap->entity();
wrap->toggle(false, anim::type::instant);
const auto session = &participants.front()->session();
const auto emojiMargin = QMargins(
-st::moderateBoxExpandInnerSkip,
-st::moderateBoxExpandInnerSkip / 2,
0,
0);
const auto emojiUp = Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::moderateBoxExpandIcon,
emojiMargin,
false));
const auto emojiDown = Ui::Text::SingleCustomEmoji(
session->data().customEmojiManager().registerInternalEmoji(
st::moderateBoxExpandIconDown,
emojiMargin,
false));
auto label = object_ptr<Ui::FlatLabel>(
inner,
QString(),
st::moderateBoxDividerLabel);
const auto raw = label.data();
auto &lifetime = wrap->lifetime();
const auto scrollLifetime = lifetime.make_state<rpl::lifetime>();
label->setClickHandlerFilter([=](
const ClickHandlerPtr &handler,
Qt::MouseButton button) {
if (button != Qt::LeftButton) {
return false;
}
wrap->toggle(!wrap->toggled(), anim::type::normal);
{
inner->heightValue() | rpl::start_with_next([=] {
if (!wrap->animating()) {
scrollLifetime->destroy();
Ui::PostponeCall(crl::guard(box, [=] {
box->scrollToY(std::numeric_limits<int>::max());
}));
} else {
box->scrollToY(std::numeric_limits<int>::max());
}
}, *scrollLifetime);
}
return true;
});
wrap->toggledValue(
) | rpl::map([isSingle, emojiUp, emojiDown](bool toggled) {
return ((toggled && isSingle)
? tr::lng_restrict_user_part
: (toggled && !isSingle)
? tr::lng_restrict_users_part
: isSingle
? tr::lng_restrict_user_full
: tr::lng_restrict_users_full)(
lt_emoji,
rpl::single(toggled ? emojiUp : emojiDown),
Ui::Text::WithEntities);
}) | rpl::flatten_latest(
) | rpl::start_with_next([=](const TextWithEntities &text) {
raw->setMarkedText(
Ui::Text::Link(text, u"internal:"_q),
Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [=] { raw->update(); },
});
}, label->lifetime());
Ui::AddSkip(inner);
inner->add(object_ptr<Ui::DividerLabel>(
inner,
std::move(label),
st::defaultBoxDividerLabelPadding,
RectPart::Top | RectPart::Bottom));
using Flag = ChatRestriction;
using Flags = ChatRestrictions;
const auto peer = items.front()->history()->peer;
const auto chat = peer->asChat();
const auto channel = peer->asChannel();
const auto defaultRestrictions = chat
? chat->defaultRestrictions()
: channel->defaultRestrictions();
const auto prepareFlags = FixDependentRestrictions(
defaultRestrictions
| ((channel && channel->isPublic())
? (Flag::ChangeInfo | Flag::PinMessages)
: Flags(0)));
const auto disabledMessages = [&] {
auto result = base::flat_map<Flags, QString>();
{
const auto disabled = FixDependentRestrictions(
defaultRestrictions
| ((channel && channel->isPublic())
? (Flag::ChangeInfo | Flag::PinMessages)
: Flags(0)));
result.emplace(
disabled,
tr::lng_rights_restriction_for_all(tr::now));
}
return result;
}();
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
box,
rpl::conditional(
rpl::single(isSingle),
tr::lng_restrict_users_part_single_header(),
tr::lng_restrict_users_part_header(
lt_count,
rpl::single(participants.size()) | tr::to_count())),
prepareFlags,
disabledMessages,
{ .isForum = peer->isForum() });
std::move(changes) | rpl::start_with_next([=] {
ban->setChecked(true);
}, ban->lifetime());
Ui::AddSkip(container);
Ui::AddDivider(container);
Ui::AddSkip(container);
container->add(std::move(checkboxes));
handleConfirmation(ban, controller, [=](
not_null<PeerData*> peer,
not_null<ChannelData*> channel) {
if (wrap->toggled()) {
Api::ChatParticipants::Restrict(
channel,
peer,
ChatRestrictionsInfo(), // Unused.
ChatRestrictionsInfo(getRestrictions(), 0),
nullptr,
nullptr);
} else {
channel->session().api().chatParticipants().kick(
channel,
peer,
{ channel->restrictions(), 0 });
}
});
}
const auto close = crl::guard(box, [=] { box->closeBox(); });
{
const auto data = &participants.front()->session().data();
const auto ids = data->itemsToIds(items);
box->addButton(tr::lng_box_delete(), [=] {
confirms->fire({});
if (confirmed) {
confirmed();
}
data->histories().deleteMessages(ids, true);
data->sendHistoryChangeNotifications();
close();
});
}
box->addButton(tr::lng_cancel(), close);
}
bool CanCreateModerateMessagesBox(const HistoryItemsList &items) {
const auto options = CalculateModerateOptions(items);
return (options.allCanBan || options.allCanDelete)
&& !options.participants.empty();
}
void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
const auto container = box->verticalLayout();
const auto maybeUser = peer->asUser();
Ui::AddSkip(container);
Ui::AddSkip(container);
base::install_event_filter(box, [=](not_null<QEvent*> event) {
if (event->type() == QEvent::KeyPress) {
if (const auto k = static_cast<QKeyEvent*>(event.get())) {
if ((k->key() == Qt::Key_Enter)
|| (k->key() == Qt::Key_Return)) {
box->uiShow()->show(Ui::MakeConfirmBox({
.text = tr::lng_gigagroup_warning_title(),
.confirmed = [=](Fn<void()> close) {
box->triggerButton(0);
close();
},
.confirmText = tr::lng_box_yes(),
.cancelText = tr::lng_box_no(),
}));
}
}
}
return base::EventFilterResult::Continue;
});
const auto line = container->add(object_ptr<Ui::RpWidget>(container));
const auto &st = st::mainMenuUserpic;
line->resize(line->width(), st.size.height());
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
line,
peer,
st);
userpic->showSavedMessagesOnSelf(true);
const auto label = Ui::CreateChild<Ui::FlatLabel>(
line,
peer->isSelf()
? tr::lng_saved_messages() | Ui::Text::ToBold()
: maybeUser
? tr::lng_profile_delete_conversation() | Ui::Text::ToBold()
: rpl::single(Ui::Text::Bold(peer->name())) | rpl::type_erased(),
box->getDelegate()->style().title);
line->widthValue(
) | rpl::start_with_next([=](int width) {
userpic->moveToLeft(st::boxRowPadding.left(), 0);
const auto skip = st::defaultBoxCheckbox.textPosition.x();
label->resizeToWidth(width
- rect::right(userpic)
- skip
- st::boxRowPadding.right());
label->moveToLeft(
rect::right(userpic) + skip,
((userpic->height() - label->height()) / 2));
}, label->lifetime());
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
label->setAttribute(Qt::WA_TransparentForMouseEvents);
Ui::AddSkip(container);
Ui::AddSkip(container);
box->addRow(
object_ptr<Ui::FlatLabel>(
container,
peer->isSelf()
? tr::lng_sure_delete_saved_messages()
: maybeUser
? tr::lng_sure_delete_history(
lt_contact,
rpl::single(peer->name()))
: (peer->isChannel() && !peer->isMegagroup())
? tr::lng_sure_leave_channel()
: tr::lng_sure_leave_group(),
st::boxLabel));
const auto maybeCheckbox = [&]() -> Ui::Checkbox* {
if (!peer->canRevokeFullHistory()) {
return nullptr;
}
Ui::AddSkip(container);
Ui::AddSkip(container);
return box->addRow(
object_ptr<Ui::Checkbox>(
container,
maybeUser
? tr::lng_delete_for_other_check(
tr::now,
lt_user,
TextWithEntities{ maybeUser->firstName },
Ui::Text::RichLangValue)
: tr::lng_delete_for_everyone_check(
tr::now,
Ui::Text::WithEntities),
false,
st::defaultBoxCheckbox));
}();
Ui::AddSkip(container);
auto buttonText = maybeUser
? tr::lng_box_delete()
: !maybeCheckbox
? tr::lng_box_leave()
: maybeCheckbox->checkedValue() | rpl::map([](bool checked) {
return checked ? tr::lng_box_delete() : tr::lng_box_leave();
}) | rpl::flatten_latest();
const auto close = crl::guard(box, [=] { box->closeBox(); });
box->addButton(std::move(buttonText), [=] {
const auto revoke = maybeCheckbox && maybeCheckbox->checked();
Core::App().closeChatFromWindows(peer);
// Don't delete old history by default,
// because Android app doesn't.
//
//if (const auto from = peer->migrateFrom()) {
// peer->session().api().deleteConversation(from, false);
//}
peer->session().api().deleteConversation(peer, revoke);
close();
}, st::attentionBoxButton);
box->addButton(tr::lng_cancel(), close);
}

View File

@@ -0,0 +1,23 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class PeerData;
namespace Ui {
class GenericBox;
} // namespace Ui
void CreateModerateMessagesBox(
not_null<Ui::GenericBox*> box,
const HistoryItemsList &items,
Fn<void()> confirmed);
[[nodiscard]] bool CanCreateModerateMessagesBox(const HistoryItemsList &);
void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer);

View File

@@ -740,7 +740,7 @@ void PasscodeBox::submitOnlyCheckCloudPassword(const QString &oldPassword) {
void PasscodeBox::sendOnlyCheckCloudPassword(const QString &oldPassword) {
checkPassword(oldPassword, [=](const Core::CloudPasswordResult &check) {
if (const auto onstack = _cloudFields.customCheckCallback) {
onstack(check);
onstack(check, Ui::MakeWeak(this));
} else {
Assert(_cloudFields.turningOff);
sendClearCloudPassword(check);

View File

@@ -51,7 +51,10 @@ public:
TimeId pendingResetDate = 0;
// Check cloud password for some action.
Fn<void(const Core::CloudPasswordResult &)> customCheckCallback;
using CustomCheck = Fn<void(
const Core::CloudPasswordResult &,
QPointer<PasscodeBox>)>;
CustomCheck customCheckCallback;
rpl::producer<QString> customTitle;
std::optional<QString> customDescription;
rpl::producer<QString> customSubmitButton;

View File

@@ -734,6 +734,10 @@ auto PeerListRow::generateNameWords() const
return peer()->nameWords();
}
const style::PeerListItem &PeerListRow::computeSt(
const style::PeerListItem &st) const {
return st;
}
void PeerListRow::invalidatePixmapsCache() {
if (_checkbox) {
@@ -816,9 +820,14 @@ void PeerListRow::stopLastRipple() {
}
}
void PeerListRow::paintRipple(Painter &p, int x, int y, int outerWidth) {
void PeerListRow::paintRipple(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int outerWidth) {
if (_ripple) {
_ripple->paint(p, x, y, outerWidth);
_ripple->paint(p, x, y, outerWidth, &st.button.ripple.color->c);
if (_ripple->empty()) {
_ripple.reset();
}
@@ -1039,11 +1048,9 @@ void PeerListContent::changeCheckState(
not_null<PeerListRow*> row,
bool checked,
anim::type animated) {
row->setChecked(
checked,
_st.item.checkbox,
animated,
[=] { updateRow(row); });
row->setChecked(checked, _st.item.checkbox, animated, [=] {
updateRow(row);
});
}
void PeerListContent::setRowHidden(not_null<PeerListRow*> row, bool hidden) {
@@ -1691,7 +1698,9 @@ crl::time PeerListContent::paintRow(
const auto row = getRow(index);
Assert(row != nullptr);
row->lazyInitialize(_st.item);
const auto &st = row->computeSt(_st.item);
row->lazyInitialize(st);
const auto outerWidth = width();
auto refreshStatusAt = row->refreshStatusTime();
@@ -1719,8 +1728,8 @@ crl::time PeerListContent::paintRow(
const auto opacity = row->opacity();
const auto &bg = selected
? _st.item.button.textBgOver
: _st.item.button.textBg;
? st.button.textBgOver
: st.button.textBg;
if (opacity < 1.) {
p.setOpacity(opacity);
}
@@ -1731,36 +1740,37 @@ crl::time PeerListContent::paintRow(
});
p.fillRect(0, 0, outerWidth, _rowHeight, bg);
row->paintRipple(p, 0, 0, outerWidth);
row->paintRipple(p, st, 0, 0, outerWidth);
row->paintUserpic(
p,
_st.item,
_st.item.photoPosition.x(),
_st.item.photoPosition.y(),
st,
st.photoPosition.x(),
st.photoPosition.y(),
outerWidth);
p.setPen(st::contactsNameFg);
const auto skipRight = _st.item.photoPosition.x();
const auto skipRight = st.photoPosition.x();
const auto rightActionSize = row->rightActionSize();
const auto rightActionMargins = rightActionSize.isEmpty()
? QMargins()
: row->rightActionMargins();
const auto &name = row->name();
const auto namex = _st.item.namePosition.x();
const auto namey = _st.item.namePosition.y();
const auto namePosition = st.namePosition;
const auto namex = namePosition.x();
const auto namey = namePosition.y();
auto namew = outerWidth - namex - skipRight;
if (!rightActionSize.isEmpty()
&& (namey < rightActionMargins.top() + rightActionSize.height())
&& (namey + _st.item.nameStyle.font->height
&& (namey + st.nameStyle.font->height
> rightActionMargins.top())) {
namew -= rightActionMargins.left()
+ rightActionSize.width()
+ rightActionMargins.right()
- skipRight;
}
const auto statusx = _st.item.statusPosition.x();
const auto statusy = _st.item.statusPosition.y();
const auto statusx = st.statusPosition.x();
const auto statusy = st.statusPosition.y();
auto statusw = outerWidth - statusx - skipRight;
if (!rightActionSize.isEmpty()
&& (statusy < rightActionMargins.top() + rightActionSize.height())
@@ -1782,7 +1792,7 @@ crl::time PeerListContent::paintRow(
width(),
selected);
auto nameCheckedRatio = row->disabled() ? 0. : row->checkedRatio();
p.setPen(anim::pen(_st.item.nameFg, _st.item.nameFgChecked, nameCheckedRatio));
p.setPen(anim::pen(st.nameFg, st.nameFgChecked, nameCheckedRatio));
name.drawLeftElided(p, namex, namey, namew, width());
p.setFont(st::contactsStatusFont);
@@ -1801,17 +1811,17 @@ crl::time PeerListContent::paintRow(
if (highlightedWidth > availableWidth) {
highlightedPart = st::contactsStatusFont->elided(highlightedPart, availableWidth);
}
p.setPen(_st.item.statusFgActive);
p.setPen(st.statusFgActive);
p.drawTextLeft(statusx, statusy, width(), highlightedPart);
} else {
grayedPart = st::contactsStatusFont->elided(grayedPart, availableWidth - highlightedWidth);
p.setPen(_st.item.statusFgActive);
p.setPen(st.statusFgActive);
p.drawTextLeft(statusx, statusy, width(), highlightedPart);
p.setPen(selected ? _st.item.statusFgOver : _st.item.statusFg);
p.setPen(selected ? st.statusFgOver : st.statusFg);
p.drawTextLeft(statusx + highlightedWidth, statusy, width(), grayedPart);
}
} else {
row->paintStatusText(p, _st.item, statusx, statusy, statusw, width(), selected);
row->paintStatusText(p, st, statusx, statusy, statusw, width(), selected);
}
row->elementsPaint(
@@ -1907,10 +1917,30 @@ void PeerListContent::selectSkipPage(int height, int direction) {
selectSkip(rowsToSkip * direction);
}
void PeerListContent::selectLast() {
const auto rowsCount = shownRowsCount();
const auto newSelectedIndex = rowsCount - 1;
_selected.index.value = newSelectedIndex;
_selected.element = 0;
if (newSelectedIndex >= 0) {
auto top = (newSelectedIndex > 0) ? getRowTop(RowIndex(newSelectedIndex)) : 0;
auto bottom = (newSelectedIndex + 1 < rowsCount) ? getRowTop(RowIndex(newSelectedIndex + 1)) : height();
_scrollToRequests.fire({ top, bottom });
}
update();
_selectedIndex = _selected.index.value;
}
rpl::producer<int> PeerListContent::selectedIndexValue() const {
return _selectedIndex.value();
}
int PeerListContent::selectedIndex() const {
return _selectedIndex.current();
}
bool PeerListContent::hasSelection() const {
return _selected.index.value >= 0;
}

View File

@@ -100,6 +100,8 @@ public:
-> const base::flat_set<QChar> &;
[[nodiscard]] virtual auto generateNameWords() const
-> const base::flat_set<QString> &;
[[nodiscard]] virtual const style::PeerListItem &computeSt(
const style::PeerListItem &st) const;
virtual void preloadUserpic();
@@ -228,7 +230,12 @@ public:
QPoint point,
UpdateCallback &&updateCallback);
void stopLastRipple();
void paintRipple(Painter &p, int x, int y, int outerWidth);
void paintRipple(
Painter &p,
const style::PeerListItem &st,
int x,
int y,
int outerWidth);
void paintUserpic(
Painter &p,
const style::PeerListItem &st,
@@ -601,6 +608,7 @@ public:
};
SkipResult selectSkip(int direction);
void selectSkipPage(int height, int direction);
void selectLast();
enum class Mode {
Default,
@@ -609,6 +617,7 @@ public:
void setMode(Mode mode);
[[nodiscard]] rpl::producer<int> selectedIndexValue() const;
[[nodiscard]] int selectedIndex() const;
[[nodiscard]] bool hasSelection() const;
[[nodiscard]] bool hasPressed() const;
void clearSelection();

View File

@@ -598,19 +598,17 @@ void EditAdminBox::requestTransferPassword(not_null<ChannelData*> channel) {
) | rpl::take(
1
) | rpl::start_with_next([=](const Core::CloudPasswordState &state) {
const auto box = std::make_shared<QPointer<PasscodeBox>>();
auto fields = PasscodeBox::CloudFields::From(state);
fields.customTitle = tr::lng_rights_transfer_password_title();
fields.customDescription
= tr::lng_rights_transfer_password_description(tr::now);
fields.customSubmitButton = tr::lng_passcode_submit();
fields.customCheckCallback = crl::guard(this, [=](
const Core::CloudPasswordResult &result) {
sendTransferRequestFrom(*box, channel, result);
const Core::CloudPasswordResult &result,
QPointer<PasscodeBox> box) {
sendTransferRequestFrom(box, channel, result);
});
*box = getDelegate()->show(Box<PasscodeBox>(
&channel->session(),
fields));
getDelegate()->show(Box<PasscodeBox>(&channel->session(), fields));
}, lifetime());
}

View File

@@ -166,33 +166,6 @@ void SaveChannelAdmin(
}).send();
}
void SaveChannelRestriction(
not_null<ChannelData*> channel,
not_null<PeerData*> participant,
ChatRestrictionsInfo oldRights,
ChatRestrictionsInfo newRights,
Fn<void()> onDone,
Fn<void()> onFail) {
channel->session().api().request(MTPchannels_EditBanned(
channel->inputChannel,
participant->input,
MTP_chatBannedRights(
MTP_flags(MTPDchatBannedRights::Flags::from_raw(
uint32(newRights.flags))),
MTP_int(newRights.until))
)).done([=](const MTPUpdates &result) {
channel->session().api().applyUpdates(result);
channel->applyEditBanned(participant, oldRights, newRights);
if (onDone) {
onDone();
}
}).fail([=] {
if (onFail) {
onFail();
}
}).send();
}
void SaveChatParticipantKick(
not_null<ChatData*> chat,
not_null<UserData*> user,
@@ -275,7 +248,7 @@ Fn<void(
ChatRestrictionsInfo newRights) {
const auto done = [=] { if (onDone) onDone(newRights); };
const auto saveForChannel = [=](not_null<ChannelData*> channel) {
SaveChannelRestriction(
Api::ChatParticipants::Restrict(
channel,
participant,
oldRights,

View File

@@ -312,6 +312,7 @@ PreviewWrap::PreviewWrap(
nullptr, // document
WebPageCollage(),
nullptr, // iv
nullptr, // stickerSet
0, // duration
QString(), // author
false, // hasLargeMedia

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "styles/style_layers.h"
@@ -23,6 +24,17 @@ void EditPeerHistoryVisibilityBox(
Ui::RadioenumGroup<HistoryVisibility>
>(historyVisibilitySavedValue);
const auto addButton = [=](
not_null<Ui::RpWidget*> inner,
HistoryVisibility v) {
const auto button = Ui::CreateChild<Ui::AbstractButton>(inner.get());
inner->sizeValue(
) | rpl::start_with_next([=](const QSize &s) {
button->resize(s);
}, button->lifetime());
button->setClickedCallback([=] { historyVisibility->setValue(v); });
};
box->setTitle(tr::lng_manage_history_visibility_title());
box->addButton(tr::lng_settings_save(), [=] {
savedCallback(historyVisibility->current());
@@ -31,32 +43,36 @@ void EditPeerHistoryVisibilityBox(
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
box->addSkip(st::editPeerHistoryVisibilityTopSkip);
box->addRow(object_ptr<Ui::Radioenum<HistoryVisibility>>(
const auto visible = box->addRow(object_ptr<Ui::VerticalLayout>(box));
visible->add(object_ptr<Ui::Radioenum<HistoryVisibility>>(
box,
historyVisibility,
HistoryVisibility::Visible,
tr::lng_manage_history_visibility_shown(tr::now),
st::defaultBoxCheckbox));
box->addRow(
visible->add(
object_ptr<Ui::FlatLabel>(
box,
tr::lng_manage_history_visibility_shown_about(),
st::editPeerPrivacyLabel),
st::editPeerPreHistoryLabelMargins + st::boxRowPadding);
st::editPeerPreHistoryLabelMargins);
addButton(visible, HistoryVisibility::Visible);
box->addSkip(st::editPeerHistoryVisibilityTopSkip);
box->addRow(object_ptr<Ui::Radioenum<HistoryVisibility>>(
const auto hidden = box->addRow(object_ptr<Ui::VerticalLayout>(box));
hidden->add(object_ptr<Ui::Radioenum<HistoryVisibility>>(
box,
historyVisibility,
HistoryVisibility::Hidden,
tr::lng_manage_history_visibility_hidden(tr::now),
st::defaultBoxCheckbox));
box->addRow(
hidden->add(
object_ptr<Ui::FlatLabel>(
box,
(isLegacy
? tr::lng_manage_history_visibility_hidden_legacy
: tr::lng_manage_history_visibility_hidden_about)(),
st::editPeerPrivacyLabel),
st::editPeerPreHistoryLabelMargins + st::boxRowPadding);
st::editPeerPreHistoryLabelMargins);
addButton(hidden, HistoryVisibility::Hidden);
}

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_user_names.h"
#include "main/main_session.h"
#include "ui/boxes/confirm_box.h"
#include "base/event_filter.h"
#include "boxes/peers/edit_participants_box.h"
#include "boxes/peers/edit_peer_color_box.h"
#include "boxes/peers/edit_peer_common.h"
@@ -27,6 +28,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/stickers_box.h"
#include "boxes/username_box.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "data/data_channel.h"
@@ -47,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_app_config.h"
#include "settings/settings_common.h"
#include "ui/boxes/boost_box.h"
#include "ui/controls/emoji_button.h"
#include "ui/controls/userpic_button.h"
#include "ui/rp_widget.h"
#include "ui/vertical_list.h"
@@ -61,6 +65,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/vertical_layout.h"
#include "window/window_session_controller.h"
#include "api/api_invite_links.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_layers.h"
#include "styles/style_menu_icons.h"
#include "styles/style_boxes.h"
@@ -533,7 +538,7 @@ object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
_wrap,
object_ptr<Ui::InputField>(
_wrap,
st::defaultInputField,
st::editPeerTitleField,
(_isBot
? tr::lng_dlg_new_bot_name
: _isGroup
@@ -555,6 +560,76 @@ object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
submitTitle();
}, result->entity()->lifetime());
{
const auto field = result->entity();
const auto container = _box->getDelegate()->outerContainer();
using Selector = ChatHelpers::TabbedSelector;
using PanelPtr = base::unique_qptr<ChatHelpers::TabbedPanel>;
const auto emojiPanelPtr = field->lifetime().make_state<PanelPtr>(
base::make_unique_q<ChatHelpers::TabbedPanel>(
container,
ChatHelpers::TabbedPanelDescriptor{
.ownedSelector = object_ptr<Selector>(
nullptr,
ChatHelpers::TabbedSelectorDescriptor{
.show = _navigation->uiShow(),
.st = st::defaultComposeControls.tabbed,
.level = Window::GifPauseReason::Layer,
.mode = Selector::Mode::PeerTitle,
}),
}));
const auto emojiPanel = emojiPanelPtr->get();
emojiPanel->setDesiredHeightValues(
1.,
st::emojiPanMinHeight / 2,
st::emojiPanMinHeight);
emojiPanel->hide();
emojiPanel->selector()->setCurrentPeer(_peer);
emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
Ui::InsertEmojiAtCursor(field->textCursor(), data.emoji);
field->setFocus();
}, field->lifetime());
emojiPanel->setDropDown(true);
const auto emojiToggle = Ui::CreateChild<Ui::EmojiButton>(
field,
st::defaultComposeControls.files.emoji);
emojiToggle->show();
emojiToggle->installEventFilter(emojiPanel);
emojiToggle->addClickHandler([=] { emojiPanel->toggleAnimated(); });
const auto updateEmojiPanelGeometry = [=] {
const auto parent = emojiPanel->parentWidget();
const auto global = emojiToggle->mapToGlobal({ 0, 0 });
const auto local = parent->mapFromGlobal(global);
emojiPanel->moveTopRight(
local.y() + emojiToggle->height(),
local.x() + emojiToggle->width() * 3);
};
base::install_event_filter(container, [=](not_null<QEvent*> event) {
const auto type = event->type();
if (type == QEvent::Move || type == QEvent::Resize) {
crl::on_main(field, [=] { updateEmojiPanelGeometry(); });
}
return base::EventFilterResult::Continue;
});
field->widthValue() | rpl::start_with_next([=](int width) {
const auto &p = st::editPeerTitleEmojiPosition;
emojiToggle->moveToRight(p.x(), p.y(), width);
updateEmojiPanelGeometry();
}, emojiToggle->lifetime());
base::install_event_filter(emojiToggle, [=](not_null<QEvent*> event) {
if (event->type() == QEvent::Enter) {
updateEmojiPanelGeometry();
}
return base::EventFilterResult::Continue;
});
}
_controls.title = result->entity();
return result;
}
@@ -983,8 +1058,8 @@ void Controller::fillHistoryVisibilityButton() {
: HistoryVisibility::Visible;
_channelHasLocationOriginalValue = channel && channel->hasLocation();
const auto updateHistoryVisibility =
std::make_shared<rpl::event_stream<HistoryVisibility>>();
const auto updateHistoryVisibility
= std::make_shared<rpl::event_stream<HistoryVisibility>>();
const auto boxCallback = crl::guard(this, [=](HistoryVisibility checked) {
updateHistoryVisibility->fire(std::move(checked));
@@ -1698,8 +1773,8 @@ void Controller::saveUsernamesOrder() {
channel->setUsernames(ranges::views::all(
newUsernames
) | ranges::views::transform([&](QString username) {
const auto editable =
(channel->editableUsername() == username);
const auto editable
= (channel->editableUsername() == username);
return Data::Username{
.username = std::move(username),
.active = true,

View File

@@ -227,9 +227,9 @@ QImage QrExact(const Qr::Data &data, int pixel, QColor color) {
p.drawImage(
skip,
skip,
Intro::details::TelegramLogoImage().scaled(
logoSize * style::DevicePixelRatio(),
logoSize * style::DevicePixelRatio(),
Window::LogoNoMargin().scaled(
logoSize,
logoSize,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation));
}

View File

@@ -7,30 +7,33 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/peers/edit_peer_reactions.h"
#include "apiwrap.h"
#include "base/event_filter.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_document.h"
#include "data/data_peer_values.h" // UniqueReactionsLimit.
#include "data/data_session.h"
#include "data/data_user.h"
#include "history/view/reactions/history_view_reactions_selector.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "ui/boxes/boost_box.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/checkbox.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/vertical_list.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/wrap/slide_wrap.h"
#include "window/window_session_controller.h"
#include "window/window_session_controller_link_info.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_info.h"
#include "styles/style_settings.h"
#include "styles/style_layers.h"
#include "styles/style_settings.h"
#include <QtWidgets/QTextEdit>
#include <QtGui/QTextBlock>
@@ -705,12 +708,16 @@ void EditAllowedReactionsBox(
}
};
changed(selected.empty() ? DefaultSelected() : std::move(selected), {});
Ui::AddSubsectionTitle(
reactions,
enabled
? tr::lng_manage_peer_reactions_available()
: tr::lng_manage_peer_reactions_some_title(),
st::manageGroupReactionsFieldPadding);
reactions->add(AddReactionsSelector(reactions, {
.outer = box->getDelegate()->outerContainer(),
.controller = args.navigation->parentController(),
.title = (enabled
? tr::lng_manage_peer_reactions_available()
: tr::lng_manage_peer_reactions_some_title()),
.title = tr::lng_manage_peer_reactions_available_ph(),
.list = all,
.selected = state->selected,
.callback = changed,
@@ -726,6 +733,7 @@ void EditAllowedReactionsBox(
}
});
const auto reactionsLimit = container->lifetime().make_state<int>(0);
if (!isGroup) {
AddReactionsText(
container,
@@ -733,9 +741,109 @@ void EditAllowedReactionsBox(
args.allowedCustomReactions,
state->customCount.value(),
args.askForBoosts);
const auto session = &args.navigation->parentController()->session();
const auto wrap = container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container,
object_ptr<Ui::VerticalLayout>(container)));
const auto max = Data::UniqueReactionsLimit(session->user());
const auto inactiveColor = std::make_optional(st::windowSubTextFg->c);
const auto activeColor = std::make_optional(
st::windowActiveTextFg->c);
const auto inner = wrap->entity();
Ui::AddSkip(inner);
Ui::AddSubsectionTitle(
inner,
tr::lng_manage_peer_reactions_max_title(),
st::manageGroupReactionsMaxSubtitlePadding);
Ui::AddSkip(inner);
const auto line = inner->add(
object_ptr<Ui::RpWidget>(inner),
st::boxRowPadding);
Ui::AddSkip(inner);
Ui::AddSkip(inner);
const auto left = Ui::CreateChild<Ui::FlatLabel>(
line,
QString::number(1),
st::defaultFlatLabel);
const auto center = Ui::CreateChild<Ui::FlatLabel>(
line,
st::defaultFlatLabel);
const auto right = Ui::CreateChild<Ui::FlatLabel>(
line,
QString::number(max),
st::defaultFlatLabel);
const auto slider = Ui::CreateChild<Ui::MediaSlider>(
line,
st::settingsScale);
rpl::combine(
line->sizeValue(),
left->sizeValue(),
center->sizeValue(),
right->sizeValue()
) | rpl::start_with_next([=](
const QSize &s,
const QSize &leftSize,
const QSize &centerSize,
const QSize &rightSize) {
const auto sliderHeight = st::settingsScale.seekSize.height();
line->resize(
line->width(),
leftSize.height() + sliderHeight * 2);
{
const auto r = line->rect();
slider->setGeometry(
0,
r.height() - sliderHeight * 1.5,
r.width(),
sliderHeight);
}
left->moveToLeft(0, 0);
right->moveToRight(0, 0);
center->moveToLeft((s.width() - centerSize.width()) / 2, 0);
}, line->lifetime());
const auto updateLabels = [=](int limit) {
left->setTextColorOverride((limit <= 1)
? activeColor
: inactiveColor);
center->setText(tr::lng_manage_peer_reactions_max_slider(
tr::now,
lt_count,
limit));
center->setTextColorOverride(activeColor);
right->setTextColorOverride((limit >= max)
? activeColor
: inactiveColor);
(*reactionsLimit) = limit;
};
const auto current = args.allowed.maxCount
? std::clamp(1, args.allowed.maxCount, max)
: max / 2;
slider->setPseudoDiscrete(
max,
[=](int index) { return index + 1; },
current,
updateLabels,
updateLabels);
updateLabels(current);
wrap->toggleOn(rpl::single(
optionInitial != Option::None
) | rpl::then(
state->selectorState.value(
) | rpl::map(rpl::mappers::_1 == SelectorState::Active)));
Ui::AddDividerText(inner, tr::lng_manage_peer_reactions_max_about());
}
const auto collect = [=] {
auto result = AllowedReactions();
result.maxCount = (*reactionsLimit);
if (isGroup
? (state->option.current() == Option::Some)
: (enabled->toggled())) {
@@ -783,6 +891,7 @@ void SaveAllowedReactions(
Data::ReactionToMTP
) | ranges::to<QVector<MTPReaction>>;
using Flag = MTPmessages_SetChatAvailableReactions::Flag;
using Type = Data::AllowedReactionsType;
const auto updated = (allowed.type != Type::Some)
? MTP_chatReactionsAll(MTP_flags((allowed.type == Type::Default)
@@ -792,14 +901,18 @@ void SaveAllowedReactions(
? MTP_chatReactionsNone()
: MTP_chatReactionsSome(MTP_vector<MTPReaction>(ids));
peer->session().api().request(MTPmessages_SetChatAvailableReactions(
allowed.maxCount ? MTP_flags(Flag::f_reactions_limit) : MTP_flags(0),
peer->input,
updated
updated,
MTP_int(allowed.maxCount)
)).done([=](const MTPUpdates &result) {
peer->session().api().applyUpdates(result);
auto parsed = Data::Parse(updated);
parsed.maxCount = allowed.maxCount;
if (const auto chat = peer->asChat()) {
chat->setAllowedReactions(Data::Parse(updated));
chat->setAllowedReactions(parsed);
} else if (const auto channel = peer->asChannel()) {
channel->setAllowedReactions(Data::Parse(updated));
channel->setAllowedReactions(parsed);
} else {
Unexpected("Invalid peer type in SaveAllowedReactions.");
}

View File

@@ -913,7 +913,7 @@ void PreviewBox(
auto businessOrder = Settings::BusinessFeaturesOrder(&show->session());
state->order = ranges::contains(businessOrder, descriptor.section)
? std::move(businessOrder)
: ranges::contains(businessOrder, descriptor.section)
: ranges::contains(premiumOrder, descriptor.section)
? std::move(premiumOrder)
: std::vector{ descriptor.section };
@@ -1240,8 +1240,8 @@ void DecorateListPromoBox(
box->setStyle(st::premiumPreviewDoubledLimitsBox);
box->widthValue(
) | rpl::start_with_next([=](int width) {
const auto &padding =
st::premiumPreviewDoubledLimitsBox.buttonPadding;
const auto &padding
= st::premiumPreviewDoubledLimitsBox.buttonPadding;
button->resizeToWidth(width
- padding.left()
- padding.right());

View File

@@ -90,7 +90,7 @@ QString ExtractRingtoneName(not_null<DocumentData*> document) {
}
const auto name = document->filename();
if (!name.isEmpty()) {
const auto extension = Data::FileExtension(name);
const auto extension = Core::FileExtension(name);
if (extension.isEmpty()) {
return name;
} else if (name.size() > extension.size() + 1) {

View File

@@ -234,7 +234,10 @@ void ShareBox::prepareCommentField() {
const auto field = _comment->entity();
field->submits(
) | rpl::start_with_next([=] { submit({}); }, field->lifetime());
) | rpl::start_with_next([=] {
submit({});
}, field->lifetime());
if (const auto show = uiShow(); show->valid()) {
InitMessageFieldHandlers(
_descriptor.session,
@@ -246,6 +249,14 @@ void ShareBox::prepareCommentField() {
}
field->setSubmitSettings(Core::App().settings().sendSubmitWay());
field->changes() | rpl::start_with_next([=] {
if (!field->getLastText().isEmpty()) {
setCloseByOutsideClick(false);
} else if (_inner->selected().empty()) {
setCloseByOutsideClick(true);
}
}, field->lifetime());
Ui::SendPendingMoveResizeEvents(_comment);
if (_bottomWidget) {
Ui::SendPendingMoveResizeEvents(_bottomWidget);
@@ -255,8 +266,6 @@ void ShareBox::prepareCommentField() {
void ShareBox::prepare() {
prepareCommentField();
setCloseByOutsideClick(false);
_select->resizeToWidth(st::boxWideWidth);
Ui::SendPendingMoveResizeEvents(_select);
@@ -313,6 +322,12 @@ void ShareBox::prepare() {
not_null<Data::Thread*> thread,
bool checked) {
innerSelectedChanged(thread, checked);
if (checked) {
setCloseByOutsideClick(false);
} else if (_inner->selected().empty()
&& _comment->entity()->getLastText().isEmpty()) {
setCloseByOutsideClick(true);
}
});
Ui::Emoji::SuggestionsController::Init(
@@ -1678,6 +1693,101 @@ void FastShareMessage(
Ui::LayerOption::CloseOther);
}
void FastShareLink(
not_null<Window::SessionController*> controller,
const QString &url) {
FastShareLink(controller->uiShow(), url);
}
void FastShareLink(
std::shared_ptr<Main::SessionShow> show,
const QString &url) {
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
const auto sending = std::make_shared<bool>();
auto copyCallback = [=] {
QGuiApplication::clipboard()->setText(url);
show->showToast(tr::lng_background_link_copied(tr::now));
};
auto submitCallback = [=](
std::vector<not_null<::Data::Thread*>> &&result,
TextWithTags &&comment,
Api::SendOptions options,
::Data::ForwardOptions) {
if (*sending || result.empty()) {
return;
}
const auto error = [&] {
for (const auto thread : result) {
const auto error = GetErrorTextForSending(
thread,
{ .text = &comment });
if (!error.isEmpty()) {
return std::make_pair(error, thread);
}
}
return std::make_pair(QString(), result.front());
}();
if (!error.first.isEmpty()) {
auto text = TextWithEntities();
if (result.size() > 1) {
text.append(
Ui::Text::Bold(error.second->chatListName())
).append("\n\n");
}
text.append(error.first);
if (const auto weak = *box) {
weak->getDelegate()->show(Ui::MakeConfirmBox({
.text = text,
.inform = true,
}));
}
return;
}
*sending = true;
if (!comment.text.isEmpty()) {
comment.text = url + "\n" + comment.text;
const auto add = url.size() + 1;
for (auto &tag : comment.tags) {
tag.offset += add;
}
} else {
comment.text = url;
}
auto &api = show->session().api();
for (const auto thread : result) {
auto message = Api::MessageToSend(
Api::SendAction(thread, options));
message.textWithTags = comment;
message.action.clearDraft = false;
api.sendMessage(std::move(message));
}
if (*box) {
(*box)->closeBox();
}
show->showToast(tr::lng_share_done(tr::now));
};
auto filterCallback = [](not_null<::Data::Thread*> thread) {
if (const auto user = thread->peer()->asUser()) {
if (user->canSendIgnoreRequirePremium()) {
return true;
}
}
return ::Data::CanSend(thread, ChatRestriction::SendOther);
};
*box = show->show(
Box<ShareBox>(ShareBox::Descriptor{
.session = &show->session(),
.copyCallback = std::move(copyCallback),
.submitCallback = std::move(submitCallback),
.filterCallback = std::move(filterCallback),
.premiumRequiredError = SharePremiumRequiredError(),
}),
Ui::LayerOption::KeepOther,
anim::type::normal);
}
auto SharePremiumRequiredError()
-> Fn<RecipientPremiumRequiredError(not_null<UserData*>)> {
return WritePremiumRequiredError;

View File

@@ -37,6 +37,7 @@ struct SendOptions;
namespace Main {
class Session;
class SessionShow;
} // namespace Main
namespace Dialogs {
@@ -68,6 +69,12 @@ void ShareGameScoreByHash(
void FastShareMessage(
not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item);
void FastShareLink(
not_null<Window::SessionController*> controller,
const QString &url);
void FastShareLink(
std::shared_ptr<Main::SessionShow> show,
const QString &url);
struct RecipientPremiumRequiredError;
[[nodiscard]] auto SharePremiumRequiredError()

View File

@@ -569,8 +569,8 @@ void StickerSetBox::updateButtons() {
if (!_inner->shortName().isEmpty()) {
const auto top = addTopButton(st::infoTopBarMenu);
const auto menu =
std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
const auto menu
= std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
top->setClickedCallback([=] {
*menu = base::make_unique_q<Ui::PopupMenu>(
top,
@@ -613,8 +613,8 @@ void StickerSetBox::updateButtons() {
_show->showBox(std::move(box));
}
};
const auto menu =
std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
const auto menu
= std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
top->setClickedCallback([=] {
*menu = base::make_unique_q<Ui::PopupMenu>(
top,
@@ -1032,7 +1032,7 @@ void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) {
_menu.get(),
type,
SendMenu::DefaultSilentCallback(sendSelected),
SendMenu::DefaultScheduleCallback(this, type, sendSelected),
SendMenu::DefaultScheduleCallback(_show, type, sendSelected),
SendMenu::DefaultWhenOnlineCallback(sendSelected));
const auto show = _show;

View File

@@ -7,31 +7,29 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "calls/calls_call.h"
#include "main/main_session.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "boxes/abstract_box.h"
#include "ui/boxes/confirm_box.h"
#include "ui/boxes/rate_call_box.h"
#include "calls/calls_instance.h"
#include "base/battery_saving.h"
#include "base/openssl_help.h"
#include "base/platform/base_platform_info.h"
#include "base/random.h"
#include "mtproto/mtproto_dh_utils.h"
#include "mtproto/mtproto_config.h"
#include "boxes/abstract_box.h"
#include "calls/calls_instance.h"
#include "calls/calls_panel.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "window/window_controller.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "lang/lang_keys.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
#include "media/audio/media_audio_track.h"
#include "base/platform/base_platform_info.h"
#include "calls/calls_panel.h"
#include "mtproto/mtproto_config.h"
#include "mtproto/mtproto_dh_utils.h"
#include "ui/boxes/confirm_box.h"
#include "ui/boxes/rate_call_box.h"
#include "webrtc/webrtc_create_adm.h"
#include "webrtc/webrtc_environment.h"
#include "webrtc/webrtc_video_track.h"
#include "webrtc/webrtc_create_adm.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "window/window_controller.h"
#include <tgcalls/Instance.h>
#include <tgcalls/VideoCaptureInterface.h>
@@ -945,8 +943,8 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
tgcalls::Descriptor descriptor = {
.version = versionString,
.config = tgcalls::Config{
.initializationTimeout =
serverConfig.callConnectTimeoutMs / 1000.,
.initializationTimeout
= serverConfig.callConnectTimeoutMs / 1000.,
.receiveTimeout = serverConfig.callPacketTimeoutMs / 1000.,
.dataSaving = tgcalls::DataSaving::Never,
.enableP2P = call.is_p2p_allowed(),
@@ -1072,6 +1070,7 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
Core::App().mediaDevices().setCaptureMuted(muted);
}, _instanceLifetime);
#if 0
Core::App().batterySaving().value(
) | rpl::start_with_next([=](bool isSaving) {
crl::on_main(weak, [=] {
@@ -1080,6 +1079,7 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
}
});
}, _instanceLifetime);
#endif
}
void Call::handleControllerStateChange(tgcalls::State state) {

View File

@@ -277,11 +277,11 @@ private:
MTP::Sender _api;
Type _type = Type::Outgoing;
rpl::variable<State> _state = State::Starting;
rpl::variable<RemoteAudioState> _remoteAudioState =
RemoteAudioState::Active;
rpl::variable<RemoteAudioState> _remoteAudioState
= RemoteAudioState::Active;
rpl::variable<Webrtc::VideoState> _remoteVideoState;
rpl::variable<RemoteBatteryState> _remoteBatteryState =
RemoteBatteryState::Normal;
rpl::variable<RemoteBatteryState> _remoteBatteryState
= RemoteBatteryState::Normal;
rpl::event_stream<Error> _errors;
FinishType _finishAfterRequestingCall = FinishType::None;
bool _answerAfterDhConfigReceived = false;

View File

@@ -162,8 +162,8 @@ private:
object_ptr<Ui::FlatLabel> _status;
object_ptr<Ui::RpWidget> _fingerprint = { nullptr };
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _remoteAudioMute = { nullptr };
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _remoteLowBattery =
{ nullptr };
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _remoteLowBattery
= { nullptr };
std::unique_ptr<Userpic> _userpic;
std::unique_ptr<VideoBubble> _outgoingVideoBubble;
QPixmap _bottomShadow;

View File

@@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "boxes/abstract_box.h"
#include "base/timer.h"
#include "styles/style_basic.h"
#include "styles/style_calls.h"
#include "styles/style_chat_helpers.h" // style::GroupCallUserpics
#include "styles/style_layers.h"
@@ -49,7 +50,6 @@ enum class BarState {
namespace {
constexpr auto kUpdateDebugTimeoutMs = crl::time(500);
constexpr auto kSwitchStateDuration = 120;
constexpr auto kMinorBlobAlpha = 76. / 255.;
@@ -374,7 +374,7 @@ void TopBar::initControls() {
};
_switchStateAnimation.stop();
const auto duration = (to - from) * kSwitchStateDuration;
const auto duration = (to - from) * st::universalDuration;
_switchStateAnimation.start(
_switchStateCallback,
from,

View File

@@ -25,8 +25,8 @@ const auto kSpeakerThreshold = std::vector<float>{
50.0f / kMaxVolumePercent,
150.0f / kMaxVolumePercent };
constexpr auto kVolumeStickedValues =
std::array<std::pair<float64, float64>, 7>{{
constexpr auto kVolumeStickedValues
= std::array<std::pair<float64, float64>, 7>{{
{ 25. / kMaxVolumePercent, 2. / kMaxVolumePercent },
{ 50. / kMaxVolumePercent, 2. / kMaxVolumePercent },
{ 75. / kMaxVolumePercent, 2. / kMaxVolumePercent },
@@ -93,8 +93,8 @@ MenuVolumeItem::MenuVolumeItem(
const auto volume = _localMuted
? 0
: base::SafeRound(_slider->value() * kMaxVolumePercent);
const auto muteProgress =
_crossLineAnimation.value((!volume) ? 1. : 0.);
const auto muteProgress
= _crossLineAnimation.value((!volume) ? 1. : 0.);
const auto selected = isSelected();
p.fillRect(clip, selected ? st.itemBgOver : st.itemBg);
@@ -174,8 +174,8 @@ MenuVolumeItem::MenuVolumeItem(
return;
}
if (_waitingForUpdateVolume) {
const auto localVolume =
base::SafeRound(_slider->value() * _maxVolume);
const auto localVolume
= base::SafeRound(_slider->value() * _maxVolume);
if ((localVolume != newVolume)
&& (_cloudVolume == newVolume)) {
_changeVolumeRequests.fire(int(localVolume));

View File

@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/checkbox.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/labels.h"
#include "styles/style_basic.h"
#include "styles/style_calls.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -26,7 +27,6 @@ namespace {
constexpr auto kRoundRadius = 9;
constexpr auto kMaxGroupCallLength = 40;
constexpr auto kSwitchDuration = 200;
constexpr auto kSelectDuration = 120;
class GraphicButton final : public Ui::AbstractButton {
public:
@@ -103,7 +103,7 @@ void GraphicButton::setToggled(bool value) {
[=] { update(); },
_toggled ? 0. : 1.,
_toggled ? 1. : 0.,
kSelectDuration);
st::universalDuration);
}
void GraphicButton::paintEvent(QPaintEvent *e) {

View File

@@ -587,11 +587,9 @@ void ChooseSourceProcess::setupGeometryWithParent(
not_null<QWidget*> parent) {
_window->createWinId();
const auto parentScreen = [&] {
if (!::Platform::IsWayland()) {
if (const auto screen = QGuiApplication::screenAt(
if (const auto screen = QGuiApplication::screenAt(
parent->geometry().center())) {
return screen;
}
return screen;
}
return parent->screen();
}();

View File

@@ -1108,8 +1108,6 @@ historyRecordVoiceFgOver: historyComposeIconFgOver;
historyRecordVoiceFgInactive: attentionButtonFg;
historyRecordVoiceFgActive: windowBgActive;
historyRecordVoiceFgActiveIcon: windowFgActive;
historyRecordVoiceShowDuration: 120;
historyRecordVoiceDuration: 120;
historyRecordVoice: icon {{ "chat/input_record", historyRecordVoiceFg }};
historyRecordVoiceOver: icon {{ "chat/input_record", historyRecordVoiceFgOver }};
historyRecordVoiceOnceBg: icon {{ "voice_lock/audio_once_bg", historySendIconFg }};

View File

@@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h"
#include "data/data_channel.h"
#include "data/data_document.h"
#include "data/data_file_origin.h"
#include "data/data_peer_values.h"
#include "data/stickers/data_stickers.h"
#include "data/stickers/data_custom_emoji.h"
@@ -51,12 +52,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat_helpers.h"
#include "styles/style_menu_icons.h"
#include <QtWidgets/QApplication>
namespace ChatHelpers {
namespace {
constexpr auto kCollapsedRows = 3;
constexpr auto kAppearDuration = 0.3;
constexpr auto kCustomSearchLimit = 256;
constexpr auto kColorPickerDelay = crl::time(500);
using Core::RecentEmojiId;
using Core::RecentEmojiDocument;
@@ -464,7 +468,8 @@ EmojiListWidget::EmojiListWidget(
std::move(descriptor.paused))
, _show(std::move(descriptor.show))
, _features(descriptor.features)
, _mode(descriptor.mode)
, _onlyUnicodeEmoji(descriptor.mode == Mode::PeerTitle)
, _mode(_onlyUnicodeEmoji ? Mode::Full : descriptor.mode)
, _api(&session().mtp())
, _staticCount(_mode == Mode::Full ? kEmojiSectionCount : 1)
, _premiumIcon(_mode == Mode::EmojiStatus
@@ -477,7 +482,8 @@ EmojiListWidget::EmojiListWidget(
, _overBg(st::emojiPanRadius, st().overBg)
, _collapsedBg(st::emojiPanExpand.height / 2, st().headerFg)
, _picker(this, st())
, _showPickerTimer([=] { showPicker(); }) {
, _showPickerTimer([=] { showPicker(); })
, _previewTimer([=] { showPreview(); }) {
setMouseTracking(true);
if (st().bg->c.alpha() > 0) {
setAttribute(Qt::WA_OpaquePaintEvent);
@@ -485,7 +491,8 @@ EmojiListWidget::EmojiListWidget(
if (_mode != Mode::RecentReactions
&& _mode != Mode::BackgroundEmoji
&& _mode != Mode::ChannelStatus) {
&& _mode != Mode::ChannelStatus
&& !_onlyUnicodeEmoji) {
setupSearch();
}
@@ -566,12 +573,17 @@ EmojiListWidget::~EmojiListWidget() {
void EmojiListWidget::setupSearch() {
const auto session = &_show->session();
const auto type = (_mode == Mode::EmojiStatus)
? TabbedSearchType::Status
: (_mode == Mode::UserpicBuilder)
? TabbedSearchType::ProfilePhoto
: TabbedSearchType::Emoji;
_search = MakeSearch(this, st(), [=](std::vector<QString> &&query) {
_nextSearchQuery = std::move(query);
InvokeQueued(this, [=] {
applyNextSearchQuery();
});
}, session, (_mode == Mode::EmojiStatus), _mode == Mode::UserpicBuilder);
}, session, type);
}
void EmojiListWidget::applyNextSearchQuery() {
@@ -624,6 +636,15 @@ void EmojiListWidget::applyNextSearchQuery() {
}
}
void EmojiListWidget::showPreview() {
if (const auto over = std::get_if<OverEmoji>(&_pressed)) {
if (const auto custom = lookupCustomEmoji(over)) {
_show->showMediaPreview(custom->stickerSetOrigin(), custom);
_previewShown = true;
}
}
}
std::vector<EmojiPtr> EmojiListWidget::collectPlainSearchResults() {
return SearchEmoji(_searchQuery, _searchEmoji);
}
@@ -1038,7 +1059,7 @@ void EmojiListWidget::fillRecent() {
const auto test = session().isTestMode();
for (const auto &one : list) {
const auto document = std::get_if<RecentEmojiDocument>(&one.id.data);
if (document && document->test != test) {
if (document && ((document->test != test) || _onlyUnicodeEmoji)) {
continue;
}
_recent.push_back({
@@ -1119,7 +1140,7 @@ void EmojiListWidget::fillRecentMenu(
const auto addAction = Ui::Menu::CreateAddActionCallback(menu);
const auto over = OverEmoji{ section, index };
const auto emoji = lookupOverEmoji(&over);
const auto custom = lookupCustomEmoji(index, section);
const auto custom = lookupCustomEmoji(&over);
if (custom && custom->sticker()) {
const auto sticker = custom->sticker();
const auto emoji = sticker->alt;
@@ -1492,6 +1513,11 @@ bool EmojiListWidget::checkPickerHide() {
return false;
}
DocumentData *EmojiListWidget::lookupCustomEmoji(
const OverEmoji *over) const {
return over ? lookupCustomEmoji(over->index, over->section) : nullptr;
}
DocumentData *EmojiListWidget::lookupCustomEmoji(
int index,
int section) const {
@@ -1594,13 +1620,19 @@ void EmojiListWidget::mousePressEvent(QMouseEvent *e) {
if (!Core::App().settings().hasChosenEmojiVariant(emoji)) {
showPicker();
} else {
_showPickerTimer.callOnce(500);
_previewTimer.cancel();
_showPickerTimer.callOnce(kColorPickerDelay);
}
} else if (lookupCustomEmoji(over)) {
_showPickerTimer.cancel();
_previewTimer.callOnce(QApplication::startDragTime());
}
}
}
void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
_previewTimer.cancel();
auto pressed = _pressed;
setPressed(v::null);
_lastMousePos = e->globalPos();
@@ -1625,7 +1657,10 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
_picker->hide();
}
if (v::is_null(_selected) || _selected != pressed) {
if (_previewShown) {
_previewShown = false;
return;
} else if (v::is_null(_selected) || _selected != pressed) {
return;
}
@@ -1644,7 +1679,7 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
return;
}
selectEmoji(lookupChosen(emoji, over));
} else if (const auto custom = lookupCustomEmoji(index, section)) {
} else if (const auto custom = lookupCustomEmoji(over)) {
selectCustom(lookupChosen(custom, over));
}
} else if (const auto set = std::get_if<OverSet>(&pressed)) {
@@ -2101,7 +2136,9 @@ void EmojiListWidget::refreshCustom() {
auto old = base::take(_custom);
const auto session = &this->session();
const auto premiumPossible = session->premiumPossible();
const auto premiumMayBeBought = premiumPossible
const auto onlyUnicodeEmoji = _onlyUnicodeEmoji || !premiumPossible;
const auto premiumMayBeBought = (!onlyUnicodeEmoji)
&& premiumPossible
&& !session->premium()
&& !_allowWithoutPremium;
const auto owner = &session->data();
@@ -2161,7 +2198,7 @@ void EmojiListWidget::refreshCustom() {
}
return true;
}();
if (premium && !premiumPossible) {
if (premium && onlyUnicodeEmoji) {
return;
} else if (valid) {
i->thumbnailDocument = it->second->lookupThumbnailDocument();
@@ -2195,7 +2232,7 @@ void EmojiListWidget::refreshCustom() {
}
}
}
if (premium && !premiumPossible) {
if (premium && onlyUnicodeEmoji) {
return;
}
_custom.push_back({
@@ -2478,7 +2515,9 @@ bool EmojiListWidget::eventHook(QEvent *e) {
void EmojiListWidget::updateSelected() {
if (!v::is_null(_pressed) || !v::is_null(_pickerSelected)) {
return;
if (!_previewShown) {
return;
}
}
auto newSelected = OverState{ v::null };
@@ -2537,6 +2576,13 @@ void EmojiListWidget::setSelected(OverState newSelected) {
} else {
_picker->showAnimated();
}
} else if (_previewShown && _pressed != _selected) {
if (const auto over = std::get_if<OverEmoji>(&_selected)) {
if (const auto custom = lookupCustomEmoji(over)) {
_pressed = _selected;
_show->showMediaPreview(custom->stickerSetOrigin(), custom);
}
}
}
}

View File

@@ -76,6 +76,7 @@ enum class EmojiListMode {
RecentReactions,
UserpicBuilder,
BackgroundEmoji,
PeerTitle,
};
struct EmojiListDescriptor {
@@ -287,6 +288,8 @@ private:
int index);
[[nodiscard]] EmojiPtr lookupOverEmoji(const OverEmoji *over) const;
[[nodiscard]] DocumentData *lookupCustomEmoji(
const OverEmoji *over) const;
[[nodiscard]] DocumentData *lookupCustomEmoji(
int index,
int section) const;
@@ -371,10 +374,13 @@ private:
DocumentId documentId,
uint64 setId);
void showPreview();
void applyNextSearchQuery();
const std::shared_ptr<Show> _show;
const ComposeFeatures _features;
const bool _onlyUnicodeEmoji;
Mode _mode = Mode::Full;
std::unique_ptr<Ui::TabbedSearch> _search;
MTP::Sender _api;
@@ -440,6 +446,8 @@ private:
object_ptr<EmojiColorPicker> _picker;
base::Timer _showPickerTimer;
base::Timer _previewTimer;
bool _previewShown = false;
rpl::event_stream<EmojiChosen> _chosen;
rpl::event_stream<FileChosen> _customChosen;

View File

@@ -38,7 +38,6 @@ namespace {
constexpr auto kShowExactDelay = crl::time(300);
constexpr auto kMaxNonScrolledEmoji = 7;
constexpr auto kAnimationDuration = crl::time(120);
} // namespace
@@ -528,7 +527,7 @@ void SuggestionsWidget::setSelected(int selected, anim::type animated) {
[=] { update(); },
_selected,
selected,
kAnimationDuration,
st::universalDuration,
anim::sineInOut);
if (_scrollMax > 0) {
const auto selectedMax = int(_rows.size()) - 3;
@@ -560,7 +559,7 @@ void SuggestionsWidget::scrollTo(int value, anim::type animated) {
[=] { update(); },
_scrollValue,
value,
kAnimationDuration,
st::universalDuration,
anim::sineInOut);
}
_scrollValue = value;

View File

@@ -437,8 +437,8 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
auto filterNotPassedByUsername = [this](UserData *user) -> bool {
if (PrimaryUsername(user).startsWith(_filter, Qt::CaseInsensitive)) {
const auto exactUsername =
(PrimaryUsername(user).size() == _filter.size());
const auto exactUsername
= (PrimaryUsername(user).size() == _filter.size());
return exactUsername;
}
return true;
@@ -446,8 +446,9 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) {
auto filterNotPassedByName = [&](UserData *user) -> bool {
for (const auto &nameWord : user->nameWords()) {
if (nameWord.startsWith(_filter, Qt::CaseInsensitive)) {
const auto exactUsername =
(PrimaryUsername(user).compare(_filter, Qt::CaseInsensitive) == 0);
const auto exactUsername = PrimaryUsername(user).compare(
_filter,
Qt::CaseInsensitive) == 0;
return exactUsername;
}
}
@@ -1378,7 +1379,7 @@ void FieldAutocomplete::Inner::contextMenuEvent(QContextMenuEvent *e) {
_menu,
type,
SendMenu::DefaultSilentCallback(send),
SendMenu::DefaultScheduleCallback(this, type, send),
SendMenu::DefaultScheduleCallback(_show, type, send),
SendMenu::DefaultWhenOnlineCallback(send));
if (!_menu->empty()) {

View File

@@ -394,7 +394,7 @@ base::unique_qptr<Ui::PopupMenu> GifsListWidget::fillContextMenu(
menu,
type,
SendMenu::DefaultSilentCallback(send),
SendMenu::DefaultScheduleCallback(this, type, send),
SendMenu::DefaultScheduleCallback(_show, type, send),
SendMenu::DefaultWhenOnlineCallback(send),
icons);
@@ -820,7 +820,7 @@ void GifsListWidget::setupSearch() {
: SearchEmojiSectionSetId();
refreshIcons();
searchForGifs(accumulated);
}, session);
}, session, TabbedSearchType::Emoji);
}
int32 GifsListWidget::showInlineRows(bool newResults) {

View File

@@ -547,6 +547,15 @@ void StickersListWidget::sendSearchRequest() {
}
_search->setLoading(true);
if (_searchQuery == Ui::PremiumGroupFakeEmoticon()) {
_search->setLoading(false);
_searchRequestId = 0;
_searchCache.emplace(_searchQuery, std::vector<uint64>());
showSearchResults();
return;
}
const auto hash = uint64(0);
_searchRequestId = _api.request(MTPmessages_SearchStickerSets(
MTP_flags(0),
@@ -570,10 +579,14 @@ void StickersListWidget::searchForSets(
return;
}
_filteredStickers = session().data().stickers().getListByEmoji(
std::move(emoji),
0,
true);
if (query == Ui::PremiumGroupFakeEmoticon()) {
_filteredStickers = session().data().stickers().getPremiumList(0);
} else {
_filteredStickers = session().data().stickers().getListByEmoji(
std::move(emoji),
0,
true);
}
if (_searchQuery != cleaned) {
_search->setLoading(false);
if (const auto requestId = base::take(_searchRequestId)) {
@@ -1654,7 +1667,7 @@ base::unique_qptr<Ui::PopupMenu> StickersListWidget::fillContextMenu(
menu,
type,
SendMenu::DefaultSilentCallback(send),
SendMenu::DefaultScheduleCallback(this, type, send),
SendMenu::DefaultScheduleCallback(_show, type, send),
SendMenu::DefaultWhenOnlineCallback(send),
icons);
@@ -2604,15 +2617,20 @@ void StickersListWidget::beforeHiding() {
void StickersListWidget::setupSearch() {
const auto session = &_show->session();
const auto type = (_mode == Mode::UserpicBuilder)
? TabbedSearchType::ProfilePhoto
: (_mode == Mode::ChatIntro)
? TabbedSearchType::Greeting
: TabbedSearchType::Stickers;
_search = MakeSearch(this, st(), [=](std::vector<QString> &&query) {
auto set = base::flat_set<EmojiPtr>();
auto text = ranges::accumulate(query, QString(), [](
QString a,
QString b) {
QString a,
QString b) {
return a.isEmpty() ? b : (a + ' ' + b);
});
searchForSets(std::move(text), SearchEmoji(query, set));
}, session, false, (_mode == Mode::UserpicBuilder));
}, session, type);
}
void StickersListWidget::displaySet(uint64 setId) {

View File

@@ -65,6 +65,7 @@ enum class StickersListMode {
Full,
Masks,
UserpicBuilder,
ChatIntro,
};
struct StickersListDescriptor {

View File

@@ -302,21 +302,39 @@ void TabbedSelector::Tab::saveScrollTop() {
_scrollTop = widget()->getVisibleTop();
}
[[nodiscard]] rpl::producer<std::vector<Ui::EmojiGroup>> GreetingGroupFirst(
not_null<Data::Session*> owner) {
return owner->emojiStatuses().stickerGroupsValue(
) | rpl::map([](std::vector<Ui::EmojiGroup> &&groups) {
const auto i = ranges::find(
groups,
Ui::EmojiGroupType::Greeting,
&Ui::EmojiGroup::type);
if (i != begin(groups) && i != end(groups)) {
ranges::rotate(begin(groups), i, i + 1);
}
return std::move(groups);
});
}
std::unique_ptr<Ui::TabbedSearch> MakeSearch(
not_null<Ui::RpWidget*> parent,
const style::EmojiPan &st,
Fn<void(std::vector<QString>&&)> callback,
not_null<Main::Session*> session,
bool statusCategories,
bool profilePhotoCategories) {
TabbedSearchType type) {
using Descriptor = Ui::SearchDescriptor;
const auto owner = &session->data();
auto result = std::make_unique<Ui::TabbedSearch>(parent, st, Descriptor{
.st = st.search,
.groups = (profilePhotoCategories
.groups = ((type == TabbedSearchType::ProfilePhoto)
? owner->emojiStatuses().profilePhotoGroupsValue()
: statusCategories
: (type == TabbedSearchType::Status)
? owner->emojiStatuses().statusGroupsValue()
: (type == TabbedSearchType::Stickers)
? owner->emojiStatuses().stickerGroupsValue()
: (type == TabbedSearchType::Greeting)
? GreetingGroupFirst(owner)
: owner->emojiStatuses().emojiGroupsValue()),
.customEmojiFactory = owner->customEmojiManager().factory(
Data::CustomEmojiManager::SizeTag::SetIcon,
@@ -378,7 +396,7 @@ TabbedSelector::TabbedSelector(
tabs.reserve(2);
tabs.push_back(createTab(SelectorTab::Stickers, 0));
tabs.push_back(createTab(SelectorTab::Masks, 1));
} else if (_mode == Mode::StickersOnly) {
} else if (_mode == Mode::StickersOnly || _mode == Mode::ChatIntro) {
tabs.reserve(1);
tabs.push_back(createTab(SelectorTab::Stickers, 0));
} else {
@@ -389,7 +407,9 @@ TabbedSelector::TabbedSelector(
}())
, _currentTabType(full()
? session().settings().selectorTab()
: (mediaEditor() || _mode == Mode::StickersOnly)
: (mediaEditor()
|| _mode == Mode::StickersOnly
|| _mode == Mode::ChatIntro)
? SelectorTab::Stickers
: SelectorTab::Emoji)
, _hasEmojiTab(ranges::contains(_tabs, SelectorTab::Emoji, &Tab::type))
@@ -540,6 +560,8 @@ TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type, int index) {
? EmojiMode::FullReactions
: _mode == Mode::RecentReactions
? EmojiMode::RecentReactions
: _mode == Mode::PeerTitle
? EmojiMode::PeerTitle
: EmojiMode::Full),
.customTextColor = _customTextColor,
.paused = paused,
@@ -552,7 +574,9 @@ TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type, int index) {
using Descriptor = StickersListDescriptor;
return object_ptr<StickersListWidget>(this, Descriptor{
.show = _show,
.mode = StickersMode::Full,
.mode = (_mode == Mode::ChatIntro
? StickersMode::ChatIntro
: StickersMode::Full),
.paused = paused,
.st = &_st,
.features = _features,
@@ -958,6 +982,9 @@ void TabbedSelector::beforeHiding() {
_beforeHidingCallback(_currentTabType);
}
}
if (Ui::InFocusChain(this)) {
window()->setFocus();
}
}
void TabbedSelector::afterShown() {

View File

@@ -86,6 +86,8 @@ enum class TabbedSelectorMode {
BackgroundEmoji,
FullReactions,
RecentReactions,
PeerTitle,
ChatIntro,
};
struct TabbedSelectorDescriptor {
@@ -97,13 +99,19 @@ struct TabbedSelectorDescriptor {
ComposeFeatures features;
};
enum class TabbedSearchType {
Emoji,
Status,
ProfilePhoto,
Stickers,
Greeting,
};
[[nodiscard]] std::unique_ptr<Ui::TabbedSearch> MakeSearch(
not_null<Ui::RpWidget*> parent,
const style::EmojiPan &st,
Fn<void(std::vector<QString>&&)> callback,
not_null<Main::Session*> session,
bool statusCategories = false,
bool profilePhotoCategories = false);
TabbedSearchType type);
class TabbedSelector : public Ui::RpWidget {
public:

View File

@@ -224,7 +224,7 @@ PreviewWrap::PreviewWrap(
lt_user,
rpl::single(
item->history()->peer->shortName()
) | rpl::map(Ui::Text::RichLangValue),
) | Ui::Text::ToRichLangValue(),
Ui::Text::RichLangValue)
: (isRound
? tr::lng_ttl_round_tooltip_in

View File

@@ -241,23 +241,21 @@ Application::~Application() {
_mediaControlsManager = nullptr;
Media::Player::finish(_audio.get());
style::stopManager();
ThirdParty::finish();
style::StopManager();
Instance = nullptr;
}
void Application::run() {
style::internal::StartFonts();
ThirdParty::start();
// Depends on OpenSSL on macOS, so on ThirdParty::start().
// Depends on notifications settings.
_notifications = std::make_unique<Window::Notifications::System>();
startLocalStorage();
style::SetCustomFont(settings().customFontFamily());
style::internal::StartFonts();
ValidateScale();
refreshGlobalProxy(); // Depends on app settings being read.
@@ -280,7 +278,7 @@ void Application::run() {
_translator = std::make_unique<Lang::Translator>();
QCoreApplication::instance()->installTranslator(_translator.get());
style::startManager(cScale());
style::StartManager(cScale());
Ui::InitTextOptions();
Ui::StartCachedCorners();
Ui::Emoji::Init();
@@ -933,8 +931,8 @@ void Application::handleAppDeactivated() {
}
rpl::producer<bool> Application::appDeactivatedValue() const {
const auto &app =
static_cast<QGuiApplication*>(QCoreApplication::instance());
const auto &app
= static_cast<QGuiApplication*>(QCoreApplication::instance());
return rpl::single(
app->applicationState()
) | rpl::then(
@@ -1525,14 +1523,14 @@ void Application::closeChatFromWindows(not_null<PeerData*> peer) {
}
}
if (const auto window = windowFor(&peer->account())) {
const auto primary = window->sessionController();
if ((primary->activeChatCurrent().peer() == peer)
&& (&primary->session() == &peer->session())) {
primary->clearSectionStack();
}
if (const auto forum = primary->shownForum().current()) {
if (peer->forum() == forum) {
primary->closeForum();
if (const auto primary = window->sessionController()) {
if (primary->activeChatCurrent().peer() == peer) {
primary->clearSectionStack();
}
if (const auto forum = primary->shownForum().current()) {
if (peer->forum() == forum) {
primary->closeForum();
}
}
}
}

View File

@@ -313,8 +313,8 @@ CloudPasswordState ParseCloudPasswordState(
ParseCloudPasswordAlgo(data.vnew_algo()));
result.mtp.newSecureSecret = ValidateNewSecureSecretAlgo(
ParseSecureSecretAlgo(data.vnew_secure_algo()));
result.unconfirmedPattern =
qs(data.vemail_unconfirmed_pattern().value_or_empty());
result.unconfirmedPattern = qs(
data.vemail_unconfirmed_pattern().value_or_empty());
result.pendingResetDate = data.vpending_reset_date().value_or_empty();
result.outdatedClient = [&] {

View File

@@ -156,6 +156,10 @@ QByteArray Settings::serialize() const {
const auto &recentEmojiPreloadData = _recentEmojiPreload.empty()
? recentEmojiPreloadGenerated
: _recentEmojiPreload;
const auto noWarningExtensions = QStringList(
begin(_noWarningExtensions),
end(_noWarningExtensions)
).join(' ');
auto size = Serialize::bytearraySize(themesAccentColors)
+ sizeof(qint32) * 5
@@ -212,7 +216,9 @@ QByteArray Settings::serialize() const {
+ Serialize::stringSize(_captureDeviceId.current())
+ Serialize::stringSize(_callPlaybackDeviceId.current())
+ Serialize::stringSize(_callCaptureDeviceId.current())
+ Serialize::bytearraySize(ivPosition);
+ Serialize::bytearraySize(ivPosition)
+ Serialize::stringSize(noWarningExtensions)
+ Serialize::stringSize(_customFontFamily);
auto result = QByteArray();
result.reserve(size);
@@ -252,7 +258,7 @@ QByteArray Settings::serialize() const {
<< qint32(_sendSubmitWay)
<< qint32(_includeMutedCounter ? 1 : 0)
<< qint32(_countUnreadMessages ? 1 : 0)
<< qint32(_exeLaunchWarning ? 1 : 0)
<< qint32(1) // legacy exe launch warning
<< qint32(_notifyAboutPinned.current() ? 1 : 0)
<< qint32(_loopAnimatedStickers ? 1 : 0)
<< qint32(_largeEmoji.current() ? 1 : 0)
@@ -357,7 +363,9 @@ QByteArray Settings::serialize() const {
<< _captureDeviceId.current()
<< _callPlaybackDeviceId.current()
<< _callCaptureDeviceId.current()
<< ivPosition;
<< ivPosition
<< noWarningExtensions
<< _customFontFamily;
}
Ensures(result.size() == size);
@@ -406,7 +414,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
qint32 sendSubmitWay = static_cast<qint32>(_sendSubmitWay);
qint32 includeMutedCounter = _includeMutedCounter ? 1 : 0;
qint32 countUnreadMessages = _countUnreadMessages ? 1 : 0;
qint32 exeLaunchWarning = _exeLaunchWarning ? 1 : 0;
std::optional<QString> noWarningExtensions;
qint32 legacyExeLaunchWarning = 1;
qint32 notifyAboutPinned = _notifyAboutPinned.current() ? 1 : 0;
qint32 loopAnimatedStickers = _loopAnimatedStickers ? 1 : 0;
qint32 largeEmoji = _largeEmoji.current() ? 1 : 0;
@@ -474,6 +483,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
qint32 trayIconMonochrome = (_trayIconMonochrome.current() ? 1 : 0);
qint32 ttlVoiceClickTooltipHidden = _ttlVoiceClickTooltipHidden.current() ? 1 : 0;
QByteArray ivPosition;
QString customFontFamily = _customFontFamily;
stream >> themesAccentColors;
if (!stream.atEnd()) {
@@ -513,7 +523,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
>> sendSubmitWay
>> includeMutedCounter
>> countUnreadMessages
>> exeLaunchWarning
>> legacyExeLaunchWarning
>> notifyAboutPinned
>> loopAnimatedStickers
>> largeEmoji
@@ -755,6 +765,13 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) {
stream >> ivPosition;
}
if (!stream.atEnd()) {
noWarningExtensions = QString();
stream >> *noWarningExtensions;
}
if (!stream.atEnd()) {
stream >> customFontFamily;
}
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()"));
@@ -817,7 +834,12 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
}
_includeMutedCounter = (includeMutedCounter == 1);
_countUnreadMessages = (countUnreadMessages == 1);
_exeLaunchWarning = (exeLaunchWarning == 1);
if (noWarningExtensions) {
const auto list = noWarningExtensions->mid(0, 10240)
.split(' ', Qt::SkipEmptyParts)
.mid(0, 1024);
_noWarningExtensions = base::flat_set<QString>(list.begin(), list.end());
}
_ipRevealWarning = (ipRevealWarning == 1);
_notifyAboutPinned = (notifyAboutPinned == 1);
_loopAnimatedStickers = (loopAnimatedStickers == 1);
@@ -956,6 +978,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!ivPosition.isEmpty()) {
_ivPosition = Deserialize(ivPosition);
}
_customFontFamily = customFontFamily;
}
QString Settings::getSoundPath(const QString &key) const {
@@ -1283,7 +1306,7 @@ void Settings::resetOnLastLogout() {
//_sendSubmitWay = Ui::InputSubmitSettings::Enter;
_soundOverrides = {};
_exeLaunchWarning = true;
_noWarningExtensions.clear();
_ipRevealWarning = true;
_loopAnimatedStickers = true;
_largeEmoji = true;

View File

@@ -398,11 +398,12 @@ public:
}
[[nodiscard]] QString getSoundPath(const QString &key) const;
[[nodiscard]] bool exeLaunchWarning() const {
return _exeLaunchWarning;
[[nodiscard]] auto noWarningExtensions() const
-> const base::flat_set<QString> & {
return _noWarningExtensions;
}
void setExeLaunchWarning(bool warning) {
_exeLaunchWarning = warning;
void setNoWarningExtensions(base::flat_set<QString> extensions) {
_noWarningExtensions = std::move(extensions);
}
[[nodiscard]] bool ipRevealWarning() const {
return _ipRevealWarning;
@@ -870,6 +871,13 @@ public:
_ivPosition = position;
}
[[nodiscard]] QString customFontFamily() const {
return _customFontFamily;
}
void setCustomFontFamily(const QString &value) {
_customFontFamily = value;
}
[[nodiscard]] static bool ThirdColumnByDefault();
[[nodiscard]] static float64 DefaultDialogsWidthRatio();
@@ -933,7 +941,7 @@ private:
Ui::SendFilesWay _sendFilesWay = Ui::SendFilesWay();
Ui::InputSubmitSettings _sendSubmitWay = Ui::InputSubmitSettings();
base::flat_map<QString, QString> _soundOverrides;
bool _exeLaunchWarning = true;
base::flat_set<QString> _noWarningExtensions;
bool _ipRevealWarning = true;
bool _loopAnimatedStickers = true;
rpl::variable<bool> _largeEmoji = true;
@@ -984,8 +992,8 @@ private:
#else // Q_OS_MAC
bool _hardwareAcceleratedVideo = false;
#endif // Q_OS_MAC
HistoryView::DoubleClickQuickAction _chatQuickAction =
HistoryView::DoubleClickQuickAction();
HistoryView::DoubleClickQuickAction _chatQuickAction
= HistoryView::DoubleClickQuickAction();
bool _translateButtonEnabled = false;
rpl::variable<bool> _translateChatEnabled = true;
rpl::variable<int> _translateToRaw = 0;
@@ -998,6 +1006,7 @@ private:
rpl::variable<bool> _storiesClickTooltipHidden = false;
rpl::variable<bool> _ttlVoiceClickTooltipHidden = false;
WindowPosition _ivPosition;
QString _customFontFamily;
bool _tabbedReplacedWithInfo = false; // per-window
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window

View File

@@ -95,9 +95,6 @@ SettingsProxy::SettingsProxy()
}
QByteArray SettingsProxy::serialize() const {
auto result = QByteArray();
auto stream = QDataStream(&result, QIODevice::WriteOnly);
const auto serializedSelected = SerializeProxyData(_selected);
const auto serializedList = ranges::views::all(
_list
@@ -111,9 +108,7 @@ QByteArray SettingsProxy::serialize() const {
0,
ranges::plus(),
&Serialize::bytearraySize);
result.reserve(size);
stream.setVersion(QDataStream::Qt_5_1);
auto stream = Serialize::ByteArrayWriter(size);
stream
<< qint32(_tryIPv6 ? 1 : 0)
<< qint32(_useProxyForCalls ? 1 : 0)
@@ -123,9 +118,7 @@ QByteArray SettingsProxy::serialize() const {
for (const auto &i : serializedList) {
stream << i;
}
stream.device()->close();
return result;
return std::move(stream).result();
}
bool SettingsProxy::setFromSerialized(const QByteArray &serialized) {
@@ -133,7 +126,7 @@ bool SettingsProxy::setFromSerialized(const QByteArray &serialized) {
return true;
}
auto stream = QDataStream(serialized);
auto stream = Serialize::ByteArrayReader(serialized);
auto tryIPv6 = qint32(_tryIPv6 ? 1 : 0);
auto useProxyForCalls = qint32(_useProxyForCalls ? 1 : 0);
@@ -148,7 +141,7 @@ bool SettingsProxy::setFromSerialized(const QByteArray &serialized) {
>> settings
>> selectedProxy
>> listCount;
if (stream.status() == QDataStream::Ok) {
if (stream.ok()) {
for (auto i = 0; i != listCount; ++i) {
QByteArray data;
stream >> data;
@@ -157,7 +150,7 @@ bool SettingsProxy::setFromSerialized(const QByteArray &serialized) {
}
}
if (stream.status() != QDataStream::Ok) {
if (!stream.ok()) {
LOG(("App Error: "
"Bad data for Core::SettingsProxy::setFromSerialized()"));
return false;

View File

@@ -32,8 +32,6 @@ constexpr auto kDefaultProxyPort = 80;
PreLaunchWindow *PreLaunchWindowInstance = nullptr;
PreLaunchWindow::PreLaunchWindow(QString title) {
style::internal::StartFonts();
setWindowIcon(Window::CreateIcon());
setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
@@ -99,7 +97,7 @@ PreLaunchWindow::~PreLaunchWindow() {
PreLaunchLabel::PreLaunchLabel(QWidget *parent) : QLabel(parent) {
QFont labelFont(font());
labelFont.setFamily(style::internal::GetFontOverride(style::internal::FontSemibold));
labelFont.setWeight(QFont::DemiBold);
labelFont.setPixelSize(static_cast<PreLaunchWindow*>(parent)->basicSize());
setFont(labelFont);
@@ -118,7 +116,6 @@ void PreLaunchLabel::setText(const QString &text) {
PreLaunchInput::PreLaunchInput(QWidget *parent, bool password) : QLineEdit(parent) {
QFont logFont(font());
logFont.setFamily(style::internal::GetFontOverride());
logFont.setPixelSize(static_cast<PreLaunchWindow*>(parent)->basicSize());
setFont(logFont);
@@ -139,7 +136,6 @@ PreLaunchInput::PreLaunchInput(QWidget *parent, bool password) : QLineEdit(paren
PreLaunchLog::PreLaunchLog(QWidget *parent) : QTextEdit(parent) {
QFont logFont(font());
logFont.setFamily(style::internal::GetFontOverride());
logFont.setPixelSize(static_cast<PreLaunchWindow*>(parent)->basicSize());
setFont(logFont);
@@ -162,7 +158,7 @@ PreLaunchButton::PreLaunchButton(QWidget *parent, bool confirm) : QPushButton(pa
setObjectName(confirm ? "confirm" : "cancel");
QFont closeFont(font());
closeFont.setFamily(style::internal::GetFontOverride(style::internal::FontSemibold));
closeFont.setWeight(QFont::DemiBold);
closeFont.setPixelSize(static_cast<PreLaunchWindow*>(parent)->basicSize());
setFont(closeFont);
@@ -181,7 +177,7 @@ PreLaunchCheckbox::PreLaunchCheckbox(QWidget *parent) : QCheckBox(parent) {
setCheckState(Qt::Checked);
QFont closeFont(font());
closeFont.setFamily(style::internal::GetFontOverride(style::internal::FontSemibold));
closeFont.setWeight(QFont::DemiBold);
closeFont.setPixelSize(static_cast<PreLaunchWindow*>(parent)->basicSize());
setFont(closeFont);

View File

@@ -159,7 +159,7 @@ void Launch(const QString &filepath) {
void ShowInFolder(const QString &filepath) {
crl::on_main([=] {
Ui::PreventDelayedActivation();
if (Platform::IsLinux()) {
if (Platform::IsX11()) {
// Hide mediaview to make other apps visible.
Core::App().hideMediaView();
}

View File

@@ -385,6 +385,7 @@ int Launcher::exec() {
// Must be started before Sandbox is created.
Platform::start();
ThirdParty::start();
auto result = executeApplication();
DEBUG_LOG(("Telegram finished, result: %1").arg(result));
@@ -400,6 +401,7 @@ int Launcher::exec() {
}
CrashReports::Finish();
ThirdParty::finish();
Platform::finish();
Logs::finish();

View File

@@ -563,6 +563,7 @@ bool ResolveUsernameOrPhone(
.phone = phone,
.messageId = post,
.storyId = storyId,
.text = params.value(u"text"_q),
.repliesInfo = commentId
? Window::RepliesByLinkInfo{
Window::CommentId{ commentId }

View File

@@ -37,6 +37,12 @@ namespace {
&& data->hasImage();
}
[[nodiscard]] base::flat_set<QString> SplitExtensions(
const QString &joined) {
const auto list = joined.split(' ');
return base::flat_set<QString>(list.begin(), list.end());
}
} // namespace
MimeType::MimeType(const QMimeType &type) : _typeStruct(type) {
@@ -162,22 +168,9 @@ bool IsMimeAcceptedForPhotoVideoAlbum(const QString &mime) {
}
bool FileIsImage(const QString &name, const QString &mime) {
QString lowermime = mime.toLower(), namelower = name.toLower();
if (lowermime.startsWith(u"image/"_q)) {
return true;
} else if (namelower.endsWith(u".bmp"_q)
|| namelower.endsWith(u".jpg"_q)
|| namelower.endsWith(u".jpeg"_q)
|| namelower.endsWith(u".gif"_q)
|| namelower.endsWith(u".webp"_q)
|| namelower.endsWith(u".tga"_q)
|| namelower.endsWith(u".tiff"_q)
|| namelower.endsWith(u".tif"_q)
|| namelower.endsWith(u".psd"_q)
|| namelower.endsWith(u".png"_q)) {
return true;
}
return false;
return name.isEmpty()
? mime.toLower().startsWith(u"image/"_q)
: (DetectNameType(name) == NameType::Image);
}
std::shared_ptr<QMimeData> ShareMimeMediaData(
@@ -194,10 +187,10 @@ std::shared_ptr<QMimeData> ShareMimeMediaData(
result->setData(u"application/x-td-use-jpeg"_q, "1");
result->setData(u"image/jpeg"_q, original->data(u"image/jpeg"_q));
}
if (auto list = Core::ReadMimeUrls(original); !list.isEmpty()) {
if (auto list = ReadMimeUrls(original); !list.isEmpty()) {
result->setUrls(std::move(list));
}
result->setText(Core::ReadMimeText(original));
result->setText(ReadMimeText(original));
return result;
}
@@ -240,4 +233,116 @@ bool CanSendFiles(not_null<const QMimeData*> data) {
return false;
}
QString FileExtension(const QString &filepath) {
const auto reversed = ranges::views::reverse(filepath);
const auto last = ranges::find_first_of(reversed, ".\\/");
if (last == reversed.end() || *last != '.') {
return QString();
}
return QString(last.base(), last - reversed.begin());
}
NameType DetectNameType(const QString &filepath) {
static const auto kImage = SplitExtensions(u"\
afdesign ai avif bmp dng gif heic icns ico jfif jpeg jpg jpg-large nef png \
png-large psd raw sketch svg tga tif tiff webp"_q);
static const auto kVideo = SplitExtensions(u"\
3g2 3gp 3gpp aep avi flv h264 m4s m4v mkv mov mp4 mpeg mpg ogv srt tgs tgv \
vob webm wmv"_q);
static const auto kAudio = SplitExtensions(u"\
aac ac3 aif amr caf cda cue flac m4a m4b mid midi mp3 ogg opus wav wma"_q);
static const auto kDocument = SplitExtensions(u"\
pdf doc docx ppt pptx pps ppsx xls xlsx txt rtf odt ods odp csv text log tl \
tex xspf xml djvu diag ps ost kml pub epub mobi cbr cbz fb2 prc ris pem p7b \
m3u m3u8 wpd wpl htm html xhtml key"_q);
static const auto kArchive = SplitExtensions(u"\
7z arj bz2 gz rar tar xz z zip zst"_q);
static const auto kThemeFile = SplitExtensions(u"\
tdesktop-theme tdesktop-palette tgios-theme attheme"_q);
static const auto kOtherBenign = SplitExtensions(u"\
c cc cpp cxx h m mm swift cs ts class java css ninja cmake patch diff plist \
gyp gitignore strings asoundrc torrent csr json xaml md keylayout sql \
sln xib mk \
\
dmg img iso vcd \
\
pdb eot ics ips ipa core mem pcap ovpn part pcapng dmp pkpass dat zxp crash \
file bak gbr plain dlc fon fnt otf ttc ttf gpx db rss cur \
\
tdesktop-endpoints"_q);
static const auto kExecutable = SplitExtensions(
#ifdef Q_OS_WIN
u"\
ad ade adp ahk app application appref-ms asp aspx asx bas bat bin cab cdxml \
cer cfg cgi chi chm cmd cnt com conf cpl crt csh der diagcab dll drv eml \
exe fon fxp gadget grp hlp hpj hta htt inf ini ins inx isp isu its jar jnlp \
job js jse jsp key ksh lexe library-ms lnk local lua mad maf mag mam \
manifest maq mar mas mat mau mav maw mcf mda mdb mde mdt mdw mdz mht mhtml \
mjs mmc mof msc msg msh msh1 msh2 msh1xml msh2xml mshxml msi msp mst ops \
osd paf pcd phar php php3 php4 php5 php7 phps php-s pht phtml pif pl plg pm \
pod prf prg ps1 ps2 ps1xml ps2xml psc1 psc2 psd1 psm1 pssc pst py py3 pyc \
pyd pyi pyo pyw pyzw pyz rb reg rgs scf scr sct search-ms settingcontent-ms \
sh shb shs slk sys swf t tmp u3p url vb vbe vbp vbs vbscript vdx vsmacros \
vsd vsdm vsdx vss vssm vssx vst vstm vstx vsw vsx vtx website wlua ws wsc \
wsf wsh xbap xll xlsm xnk xs"_q
#elif defined Q_OS_MAC // Q_OS_MAC
u"\
applescript action app bin command csh osx workflow terminal url caction \
mpkg pkg scpt scptd xhtm xhtml webarchive"_q
#else // Q_OS_WIN || Q_OS_MAC
u"bin csh deb desktop ksh out pet pkg pup rpm run sh shar slp zsh"_q
#endif // !Q_OS_WIN && !Q_OS_MAC
);
const auto extension = FileExtension(filepath).toLower();
if (kExecutable.contains(extension)) {
return NameType::Executable;
} else if (kImage.contains(extension)) {
return NameType::Image;
} else if (kVideo.contains(extension)) {
return NameType::Video;
} else if (kAudio.contains(extension)) {
return NameType::Audio;
} else if (kDocument.contains(extension)) {
return NameType::Document;
} else if (kArchive.contains(extension)) {
return NameType::Archive;
} else if (kThemeFile.contains(extension)) {
return NameType::ThemeFile;
} else if (kOtherBenign.contains(extension)) {
return NameType::OtherBenign;
}
return NameType::Unknown;
}
bool NameTypeAllowsThumbnail(NameType type) {
return type == NameType::Image
|| type == NameType::Video
|| type == NameType::Audio
|| type == NameType::Document
|| type == NameType::ThemeFile;
}
bool IsIpRevealingPath(const QString &filepath) {
static const auto kExtensions = [] {
const auto joined = u"htm html svg m4v m3u8 xhtml"_q;
const auto list = joined.split(' ');
return base::flat_set<QString>(list.begin(), list.end());
}();
static const auto kMimeTypes = [] {
const auto joined = u"text/html image/svg+xml"_q;
const auto list = joined.split(' ');
return base::flat_set<QString>(list.begin(), list.end());
}();
return ranges::binary_search(
kExtensions,
FileExtension(filepath).toLower()
) || ranges::binary_search(
kMimeTypes,
QMimeDatabase().mimeTypeForFile(QFileInfo(filepath)).name()
);
}
} // namespace Core

View File

@@ -69,4 +69,21 @@ struct MimeImageData {
[[nodiscard]] QList<QUrl> ReadMimeUrls(not_null<const QMimeData*> data);
[[nodiscard]] bool CanSendFiles(not_null<const QMimeData*> data);
enum class NameType : uchar {
Unknown,
Executable,
Image,
Video,
Audio,
Document,
Archive,
ThemeFile,
OtherBenign,
};
[[nodiscard]] QString FileExtension(const QString &filepath);
[[nodiscard]] NameType DetectNameType(const QString &filepath);
[[nodiscard]] bool NameTypeAllowsThumbnail(NameType type);
[[nodiscard]] bool IsIpRevealingPath(const QString &filepath);
} // namespace Core

View File

@@ -33,7 +33,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QSessionManager>
#include <QtGui/QScreen>
#include <QtGui/qpa/qplatformscreen.h>
#include <ksandbox.h>
namespace Core {
namespace {
@@ -83,7 +82,6 @@ bool Sandbox::QuitOnStartRequested = false;
Sandbox::Sandbox(int &argc, char **argv)
: QApplication(argc, argv)
, _mainThreadId(QThread::currentThreadId()) {
setQuitOnLastWindowClosed(false);
}
int Sandbox::start() {
@@ -518,10 +516,8 @@ void Sandbox::refreshGlobalProxy() {
|| proxy.type == MTP::ProxyData::Type::Http) {
QNetworkProxy::setApplicationProxy(
MTP::ToNetworkProxy(MTP::ToDirectIpProxy(proxy)));
} else if ((!Core::IsAppLaunched()
|| Core::App().settings().proxy().isSystem())
// this works stable only in sandboxed environment where it works through portal
&& (!Platform::IsLinux() || KSandbox::isInside() || cDebugMode())) {
} else if (!Core::IsAppLaunched()
|| Core::App().settings().proxy().isSystem()) {
QNetworkProxyFactory::setUseSystemConfiguration(true);
} else {
QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy);

View File

@@ -107,6 +107,7 @@ private:
void readClients();
void removeClients();
QEventLoopLocker _eventLoopLocker;
const Qt::HANDLE _mainThreadId = nullptr;
int _eventNestingLevel = 0;
int _loopNestingLevel = 0;

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