Compare commits

..

309 Commits

Author SHA1 Message Date
John Preston
eccaca8808 Version 3.5: Fix build with GCC. 2022-02-01 01:51:17 +03:00
John Preston
36e6c76b59 Version 3.5: Merge some lib_ui fixes. 2022-02-01 01:27:33 +03:00
John Preston
06c35f5b51 Version 3.5.
- Use a new type of detailed stickers with smooth animations.
- Create new sets by sending .webm videos to @stickers.
- Bring your custom animated stickers from other apps.
- See smaller, compact animations when reacting to messages.
- See real-time animations in chat when a user reacts to your message.
- React with additional emoji expressing love,
appreciation, anger or surprise.
- Tap the new button in chats to jump to your messages
that have unseen reactions.
- Watch the animations for unseen reactions play
when you hit the button.
- The app will warn you before closing
if you are uploading photos or files to a chat.
- Enjoy better screencast quality in video chats.
2022-02-01 00:51:00 +03:00
John Preston
ca21b7efae Allow disable Cmd+Q warning on macOS. 2022-02-01 00:43:20 +03:00
John Preston
9eb20ede33 Fix click area of web page preview article photo. 2022-02-01 00:26:52 +03:00
John Preston
656146c445 Improve ViewButton + Reactions view. 2022-02-01 00:26:34 +03:00
John Preston
2eb8ed59cc Update unread reaction icon to a heart. 2022-01-31 23:32:04 +03:00
John Preston
9d6eeace54 Fix quilt by system on macOS. 2022-01-31 23:16:56 +03:00
John Preston
315c85fb8d Always show unread mentions / reactions buttons. 2022-01-31 23:16:56 +03:00
John Preston
b7a70a2f28 Improve unread reactions list consistency. 2022-01-31 23:16:56 +03:00
John Preston
63bf564757 Reaction notifications only from non-blocked contacts. 2022-01-31 16:18:40 +03:00
John Preston
6a9c5818ba Show go-to-user buttons in t.me links preview. 2022-01-31 16:17:58 +03:00
John Preston
98e7de01b0 Improve naming in dialogs_layout module. 2022-01-31 16:17:34 +03:00
John Preston
8ef7325e16 No confirmation for graph.org / te.legra.ph domains. 2022-01-31 16:14:03 +03:00
John Preston
17de379145 Fix document thumbnails on Retina screens. 2022-01-31 13:18:52 +03:00
John Preston
eb784c665a Fix build with tg_owt@M98 on macOS. 2022-01-31 13:18:42 +03:00
John Preston
07beb3e86b Correctly clear unread reactions. 2022-01-31 11:19:46 +03:00
John Preston
18919a6b4a Fix tg_owt@M98 build on Linux. 2022-01-30 19:39:33 +03:00
John Preston
71d4b64691 Fix link / forward preview on Retina screen.
Regression was introduced in 3ff17a8789.
2022-01-30 00:34:19 +03:00
John Preston
9918a20946 Fix tg_owt@M98 build with MSVC. 2022-01-29 15:18:23 +03:00
John Preston
ae5e7d641a Build on macOS with WebRTC M98. 2022-01-29 14:06:55 +03:00
John Preston
4e88ea970e Fix build with GCC. 2022-01-29 12:47:20 +03:00
John Preston
f6ff0f3b2c Fix build for macOS 10.12+. 2022-01-28 23:52:11 +03:00
John Preston
84d58e574f Closed alpha version 3.4.8.1. 2022-01-28 19:11:13 +03:00
John Preston
1dd7cc956b Show reaction notifications in groups. 2022-01-28 19:10:07 +03:00
John Preston
54e7dfe986 Open exact message on local reaction notification. 2022-01-28 19:10:07 +03:00
John Preston
4e9a52343f Process unread reactions to unknown messages. 2022-01-28 19:10:07 +03:00
John Preston
51c805d77a Enable reading reactions on the server. 2022-01-28 19:10:07 +03:00
John Preston
8bde488662 Support multiple reaction animations in one message. 2022-01-28 19:10:07 +03:00
John Preston
4f1e04cf9e Animate reactions when reading. 2022-01-28 19:10:07 +03:00
John Preston
e509da8fd8 Instantly mark as read visible new reactions. 2022-01-28 19:10:07 +03:00
John Preston
f6bfe2c9a8 Mark all reactions as read context menu. 2022-01-28 19:10:07 +03:00
John Preston
15719b73b4 Fix updating unread message reactions. 2022-01-28 19:10:07 +03:00
John Preston
8da9638563 Count correct scroll-for-message with unread reaction. 2022-01-28 19:10:07 +03:00
John Preston
a5afeebc0c Show reaction notification by unread status. 2022-01-28 19:10:07 +03:00
John Preston
9903266722 Index new unread reactions in history. 2022-01-28 19:10:07 +03:00
John Preston
8f33d5903d Display unread reactions badge in chats list. 2022-01-28 19:10:07 +03:00
John Preston
e9c79886d2 Track unread mentions and unread reactions the same way. 2022-01-28 19:10:07 +03:00
John Preston
6207770120 Paint unread mention badge as an icon. 2022-01-28 19:10:07 +03:00
John Preston
2a99f1a1ef Add tabbed-panel-show-on-click option. 2022-01-28 19:10:07 +03:00
John Preston
4aafcebef5 Add empty experimental settings section. 2022-01-28 19:10:07 +03:00
John Preston
3a78e94f2f Don't downscale screen captured frame too much. 2022-01-28 19:10:07 +03:00
John Preston
b5aca56914 Fix pausing stickers in StickerSetBox. 2022-01-28 19:10:07 +03:00
John Preston
a6621233d0 Show first frame of webm in photo editor. 2022-01-28 19:10:07 +03:00
John Preston
b4eb25de58 Support webm stickers in StickerSetBox. 2022-01-28 19:10:07 +03:00
John Preston
d96a8d028a Support webm stickers in field autocomplete. 2022-01-28 19:10:07 +03:00
John Preston
f45c47f3d5 Limit inline results repainting rate. 2022-01-28 19:10:07 +03:00
John Preston
827ce46d3c Support webm stickers in bot inline results. 2022-01-28 19:10:07 +03:00
John Preston
91c84d63de Force libvpx_vp9 decoder for VP9 videos.
Webm stickers depend on decoder support for alpha channel.
2022-01-28 19:10:07 +03:00
John Preston
545392f90f Show webm stickers in media preview. 2022-01-28 19:10:07 +03:00
John Preston
fa61cf3c85 Show webm animation in stickers box set thumbnail. 2022-01-28 19:10:07 +03:00
John Preston
c359646702 Animate webm pack icon in the panel. 2022-01-28 19:10:07 +03:00
John Preston
51fef843f0 Optimize sets stickers / gifs panel repainting. 2022-01-28 19:10:07 +03:00
John Preston
1a3a0fb124 Fix caching of webm stickers in local storage. 2022-01-28 19:10:07 +03:00
John Preston
f1d9cca119 Fix crash and pause in stickers panel with webm. 2022-01-28 19:10:07 +03:00
John Preston
8e749173de Render webm stickers in StickersListWidget. 2022-01-28 19:10:07 +03:00
John Preston
20dbf18106 Init webm player for sticker set thumbnails. 2022-01-28 19:10:07 +03:00
John Preston
10ff71e8f6 Support svg path outline for Webm stickers. 2022-01-28 19:10:07 +03:00
John Preston
170cc77a1b Use QColor instead of optional<QColor> in lottie. 2022-01-28 19:10:07 +03:00
John Preston
589673e420 Fix non-sticker Webm in media viewer. 2022-01-28 19:10:07 +03:00
John Preston
2f9c39fe53 Support selecting Webm stickers. 2022-01-28 19:10:07 +03:00
John Preston
044c7f3ce9 Generate opaque good thumbnails for non-sticker Webm. 2022-01-28 19:10:07 +03:00
John Preston
d18e28978a Cache good thumbnail in Webp for Webm stickers. 2022-01-28 19:10:07 +03:00
John Preston
581b84afe0 Update API scheme on layer 138. 2022-01-28 19:10:07 +03:00
John Preston
846cabeda5 Premultiply YUVA alpha in FFMpegReaderImplementation. 2022-01-28 19:10:07 +03:00
John Preston
9b59ef00af Generate good video thumbnail for Webm stickers. 2022-01-28 19:10:07 +03:00
John Preston
079772a399 Correctly preserve first frame alpha in video streaming. 2022-01-28 19:10:07 +03:00
John Preston
2e39befd7c Don't trust AVFormatContext duration in Webm video.
It reports some strange numbers like 1000, which is 1ms.
2022-01-28 19:10:07 +03:00
John Preston
219ffd2c48 Handle clicks on Webm stickers. 2022-01-28 19:10:07 +03:00
John Preston
63d15e4479 Support Webm sticker display in chat history. 2022-01-28 19:10:07 +03:00
John Preston
2f01efdd64 Correctly detect Webm stickers. 2022-01-28 19:10:07 +03:00
John Preston
8b7d2c880e Support rendering Webm videos with alpha. 2022-01-28 19:10:07 +03:00
John Preston
1755ead681 Update API scheme to layer 138. 2022-01-28 19:10:07 +03:00
John Preston
1e1f7be708 Don't scroll reactions dropdown too fast.
Fixes #23985.
2022-01-28 19:08:44 +03:00
John Preston
7170bec25d Update scroll-history-to-bottom arrow icon. 2022-01-27 17:23:31 +03:00
John Preston
7ad1a7dd37 Fix userpics re-generating in message reactions. 2022-01-27 17:18:06 +03:00
John Preston
217e9b2475 Fix build with GCC. 2022-01-27 16:20:39 +03:00
John Preston
28f2c213f7 Remove app module. Support delayed quit by Cmd+Q on macOS. 2022-01-27 15:54:20 +03:00
John Preston
b8f1cebeb6 Support confirm-on-Quit on macOS. 2022-01-26 14:50:10 +03:00
John Preston
6a3ad52aef Add upload cancel confirmation on Quit and Log Out. 2022-01-26 13:15:28 +03:00
John Preston
8c349c0515 Fix non-Retina spelling error underline on macOS. 2022-01-26 12:42:12 +03:00
John Preston
9038dfb3b8 Load more reactions per page after the first request. 2022-01-26 11:57:21 +03:00
John Preston
2e94488eb4 Fix possible crash in ListWidget destructor. 2022-01-26 10:40:22 +03:00
23rd
59ed41abfe Added ability to select links and monospaced text with pressed Alt key. 2022-01-26 10:39:57 +03:00
23rd
8bea6776f5 Replaced checking global key modifiers with qt adapters. 2022-01-26 10:34:38 +03:00
23rd
0143d22a21 Split qt_adapters to separated files. 2022-01-26 10:33:28 +03:00
23rd
021d0053be Fixed hiding spoilers in captions when switching sections. 2022-01-26 10:33:20 +03:00
Ilya Fedin
443ca0b390 Don't use BYPRODUCTS with appdata changelog
Due to recursive dependency
2022-01-25 18:25:58 +04:00
Ilya Fedin
2e0224589f Update cmake_helpers 2022-01-25 16:14:01 +04:00
Ilya Fedin
f8da59595a Fix appdata changelog target name 2022-01-25 16:14:01 +04:00
Ilya Fedin
e8568c6701 Set encoding when reading changelog for appdata 2022-01-25 16:14:01 +04:00
gasinvein
4ca3f6a1b3 Ensure appdata releases tag is replaced rather than added 2022-01-25 16:14:01 +04:00
John Preston
6af255923a Use generate_target for timestamped changelog generator. 2022-01-25 14:53:26 +03:00
Ilya Fedin
624d83dc60 Update cmake_helpers 2022-01-23 18:25:37 +04:00
Ilya Fedin
6073da2843 Disable libvpx unit tests in Dockerfile & prepare.py 2022-01-23 18:25:37 +04:00
Ilya Fedin
ca5d2c115d Update tg_owt 2022-01-23 18:25:37 +04:00
John Preston
86f3d88116 Fix build for Windows and Linux. 2022-01-23 12:23:31 +03:00
John Preston
7a971b5855 Fix build for Windows x64. 2022-01-23 10:17:15 +03:00
John Preston
0a7de3340a Revert submodules downgrade. 2022-01-23 10:07:41 +03:00
John Preston
2cb73eefeb Build docker for Linux with external libvpx. 2022-01-23 01:07:36 +03:00
John Preston
152aa06930 Qt patch with a possible work-through-proxy fix. 2022-01-23 00:54:41 +03:00
John Preston
cbca78ff63 Fix build with GCC. 2022-01-23 00:27:17 +03:00
John Preston
7c46b292ac Fix build with MSVC. 2022-01-23 00:26:16 +03:00
John Preston
883509903f Fix build with Xcode. 2022-01-22 23:27:13 +03:00
Ilya Fedin
37b2951058 Update submodules 2022-01-22 21:18:37 +04:00
Ilya Fedin
ceb323ac7c Use QMenuBar instead of own global menu implementation on Linux
This is another attempt of 79f96480c2
2022-01-22 21:18:37 +04:00
Ilya Fedin
b65d40a22b Get rid of custom SNI implementation
XDG is inventing new tray specification, so SNI will be outdated soon and it's better to just use QSystemTrayIcon.
I believe all the major drawbacks of QSystemTrayIcon are solved and we can live with minor ones.
Given the planned MainWindow refactoring, it seems it's the best time to do that.
2022-01-22 21:18:37 +04:00
John Preston
aed49b9289 Build FFmpeg with libvpx on macOS. 2022-01-22 19:51:00 +03:00
John Preston
72d81cc52f Build 32 bit Windows version with external_vpx. 2022-01-22 19:50:55 +03:00
c0re100
4bb3aec168 Fix incorrect admin log
Type: Restricted to Member
2022-01-22 18:43:43 +04:00
Ilya Fedin
c0a81f2428 Use the suggested workaround for qtwayland build arguments instead of patching 2022-01-22 18:42:59 +04:00
Ilya Fedin
692adacc2a Fix support icon on Linux when system icon is present 2022-01-22 18:39:53 +04:00
John Preston
26dbeb6831 Fix build with Xcode. 2022-01-22 16:42:38 +03:00
John Preston
07f72c20eb Change application icon only in support mode.
Fixes #23895.
2022-01-22 16:40:57 +03:00
John Preston
8407b0cccf Fix buffer double-delete in Images::Blur. 2022-01-21 16:01:16 +03:00
John Preston
3ff17a8789 Refactor image transformation interfaces. 2022-01-21 15:33:44 +03:00
John Preston
a9a6d8a568 Fix creating links containing '$'.
Regression was introduced in desktop-app/lib_ui@8331322c20.

Fixes #23921.
2022-01-21 15:32:45 +03:00
John Preston
9877845b9c Don't always enable screencast logs. 2022-01-21 15:32:45 +03:00
John Preston
dc89262461 Version 3.4.8.
- Nice animations in reactions.
- Add snap layouts support on Windows 11.
- Bug fixes and other minor improvements.
2022-01-19 19:12:22 +03:00
John Preston
a5425042cf Fix possible crash when update is ready.
Regression was introduced in 9a0be43ef5.
2022-01-19 19:03:19 +03:00
John Preston
d6e03c3e48 Fix possible crash with incorrect local time. 2022-01-19 19:03:19 +03:00
John Preston
e91eecf34f Fix possible crash in empty sticker set. 2022-01-19 19:03:19 +03:00
John Preston
1a8cc87e60 Fix reaction images on Retina screen. 2022-01-19 15:40:04 +03:00
John Preston
0863941642 Beta version 3.4.7.
- Fix a crash on launch on Windows.
2022-01-19 09:34:17 +03:00
John Preston
b331aee599 Fix a crash on Windows < 11. 2022-01-19 09:33:34 +03:00
John Preston
e19180cc86 Beta version 3.4.6: Fix build with Xcode. 2022-01-18 21:56:20 +03:00
John Preston
10cb891f48 Beta version 3.4.6.
- Add snap layouts support on Windows 11.
- Fix crash in drafts after accounts switching.
2022-01-18 21:47:13 +03:00
John Preston
c8f7a8c795 Add a tab with "Who Seen" to "Who Reacted" box. 2022-01-18 21:44:59 +03:00
John Preston
74a28ffdf7 Use correct string for reacted / seen item. 2022-01-18 19:55:24 +03:00
John Preston
ecedce0c2f Fix crash in drafts saving.
Regression was introduced in 43559fb6b7.
2022-01-18 19:05:31 +03:00
John Preston
bd4f993292 Add Windows 11 snap layouts to call / voicechat. 2022-01-18 18:39:55 +03:00
John Preston
4934b026d3 Improve call / voicechat title controls on Windows 11. 2022-01-18 15:53:04 +03:00
John Preston
11f183a79f Better animate sent reaction in flipped context. 2022-01-18 14:45:28 +03:00
John Preston
ae426a41e0 Better reaction layout outside of a bubble. 2022-01-18 14:37:14 +03:00
John Preston
d6edc3728d Workaround selection glitches on macOS. 2022-01-18 14:11:15 +03:00
John Preston
e121487170 Fix appear animation when sending a reaction in a group. 2022-01-18 13:09:42 +03:00
John Preston
72a093ec77 Support Windows 11 snap layouts in the main window. 2022-01-18 12:59:54 +03:00
John Preston
4996d90782 Fix first my reaction userpic in groups. 2022-01-17 21:29:28 +03:00
John Preston
9a451a1423 Don't suggest "Set As Quick" on already quick. 2022-01-17 19:21:34 +03:00
John Preston
4d11ad45db Use common title bar buttons in Call Panel. 2022-01-17 19:21:34 +03:00
John Preston
1657c2c7f2 Fix context menu on sent images / documents. 2022-01-17 19:21:34 +03:00
John Preston
c5e7048a3d Don't copy-on-click "pre" entities. 2022-01-17 16:49:14 +03:00
John Preston
1f194da2f0 Improve macOS title bar font and rounding. 2022-01-17 16:48:32 +03:00
John Preston
0954b04f24 Fix title controls on Windows 11. 2022-01-17 13:39:14 +03:00
John Preston
4659499340 Update window title icons. 2022-01-17 11:18:12 +03:00
John Preston
6eb4584408 Fix links before monospace formatting. 2022-01-17 10:51:35 +03:00
23rd
4ba4b77b95 Fixed formatting of text in entry of archived folder.
Fixed #23906.
2022-01-17 10:12:18 +03:00
John Preston
f9bf6dbc1e Beta version 3.4.5: Fix build with GCC. 2022-01-16 23:25:08 +03:00
John Preston
64b5269648 Beta version 3.4.5.
- Fix crash in monospace blocks processing.
- Fix reaction animations stopping after an hour uptime.
2022-01-16 22:57:01 +03:00
John Preston
f394cecf55 Fix crash in monospace blocks processing.
Fixes #23905.
2022-01-16 14:13:41 +03:00
23rd
8b56676c23 Fixed formatting of some internal links. 2022-01-16 14:13:12 +03:00
23rd
e2713ea627 Added ability to create formatted internal links. 2022-01-16 14:13:10 +03:00
John Preston
f5e50409d3 Add initial reaction bubble appear animation. 2022-01-16 14:11:50 +03:00
John Preston
050916a56a Fix userpics in more than one recent reaction. 2022-01-15 22:02:10 +03:00
John Preston
cdf36cc387 Fix reaction animations stopping after an hour uptime. 2022-01-15 12:38:47 +03:00
John Preston
f909a36cbd Beta version 3.4.4: Fix incorrect changelog metadata. 2022-01-15 06:29:19 +03:00
John Preston
1d36255ca5 Beta version 3.4.4: Fix build on Windows. 2022-01-14 23:41:27 +03:00
John Preston
acfdae2d72 Beta version 3.4.4: Fix build with GCC. 2022-01-14 22:57:13 +03:00
John Preston
ab59e97b92 Beta version 3.4.4.
- Nice animations in reactions.
2022-01-14 22:06:03 +03:00
John Preston
1060b04b1e Reacted users list on inline reaction right click. 2022-01-14 21:55:48 +03:00
John Preston
df044dbd83 Show local notifications about contact reactions. 2022-01-14 21:55:48 +03:00
John Preston
5eb210ec12 "Set As Quick" context menu in reactions dropdown. 2022-01-14 21:55:48 +03:00
John Preston
f24f78c0cc Use click handler property instead of dynamic cast. 2022-01-14 21:55:48 +03:00
John Preston
8a071fe1fe Respect reactions_default from appconfig. 2022-01-14 21:55:48 +03:00
John Preston
f3e84de5fb Implement recent reaction userpics in groups. 2022-01-14 21:55:48 +03:00
John Preston
2dec1b72f7 Rename reactionBottom* to reactionInline* styles. 2022-01-14 21:55:48 +03:00
John Preston
604a827a52 Allow fast reaction revoke in private chats. 2022-01-14 21:55:48 +03:00
John Preston
3d8b303ab7 Implement parabolic reaction drop. 2022-01-14 21:55:48 +03:00
John Preston
2c599e60c3 Improve first reaction animation in group. 2022-01-14 21:55:48 +03:00
John Preston
928d8feb21 Improve clearing of reaction animations. 2022-01-14 21:55:48 +03:00
John Preston
490e688a91 Add reaction animations to comments. 2022-01-14 21:55:48 +03:00
John Preston
34c36d77c3 Paint reaction animations above everything. 2022-01-14 21:55:47 +03:00
John Preston
0ab26f0c82 Initial reaction effects implementation. 2022-01-14 21:55:47 +03:00
John Preston
db453ab7ae Allow slowing down Animations::Simple in debug build. 2022-01-14 21:55:47 +03:00
John Preston
e032dbf383 Cache reaction lottie in Window::SessionController. 2022-01-14 21:55:47 +03:00
John Preston
3b4ed03105 Image coords should be multiply devicePixelRatio.
I hope this fixes #17277.
2022-01-14 21:55:47 +03:00
John Preston
963694330d Correctly apply reaction restrictions. 2022-01-14 21:55:47 +03:00
John Preston
2733b12cff Improve popup menu dimensions. 2022-01-14 21:55:47 +03:00
John Preston
a377364621 Use correct sizes for lottie frames. 2022-01-14 21:55:47 +03:00
John Preston
58f4884deb Optimize dropdown overlay painting. 2022-01-14 21:55:47 +03:00
John Preston
c2c7a25487 Keep reaction media in memory. 2022-01-14 21:55:47 +03:00
John Preston
f98c08f4c6 Improve reaction scale animation. 2022-01-14 21:55:47 +03:00
John Preston
cfc2a959cf Implement nice dropdown collapse animation. 2022-01-14 21:55:47 +03:00
John Preston
6a1630a84c Paint gradients inside dropdown. 2022-01-14 21:55:47 +03:00
John Preston
a51be85199 Improve button and dropdown layout. 2022-01-14 21:55:47 +03:00
John Preston
e0fd5d8795 Fix reactions dropdown on Retina screen. 2022-01-14 21:55:47 +03:00
John Preston
8659f60b46 Good dropdown rounding and shadow. 2022-01-14 21:55:47 +03:00
John Preston
c43699fb43 Prepare for better clipping. 2022-01-14 21:55:47 +03:00
John Preston
c56a22c8d5 Play select animations in reactions. 2022-01-14 21:55:47 +03:00
John Preston
7f27ce6dee Scale reactions on mouse over. 2022-01-14 21:55:47 +03:00
John Preston
508ba4750c Show appear animations in reactions dropdown. 2022-01-14 21:55:47 +03:00
John Preston
c0b19000d6 Use lottie instead of webp in reactions dropdown. 2022-01-14 21:55:47 +03:00
John Preston
409a3357da Use lottie instead of webp in bottom info reactions. 2022-01-14 21:55:47 +03:00
John Preston
82523978c9 Use lottie instead of webp in Edit Chat Reactions. 2022-01-14 21:55:47 +03:00
John Preston
718ba2d0e3 Update API scheme to layer 137. 2022-01-14 21:55:47 +03:00
John Preston
2317dd8820 Update cmake_helpers submodule. 2022-01-14 21:55:47 +03:00
Hans Gaiser
df06f55c7f Use QT_QPA_PLATFORM if provided. 2022-01-14 21:58:43 +04:00
John Preston
d43853460e Update plasma-wayland-protocols to 1.6.0. 2022-01-13 19:37:22 +03:00
John Preston
28fee318d7 Update submodules. 2022-01-13 13:17:55 +03:00
23rd
1ec2ecac11 Fixed render of scene from photo editor for grayscaled images.
Fixed #23889.
2022-01-13 04:20:15 +03:00
23rd
7aa3956792 Fixed skip blocks for web pages and games. 2022-01-13 04:06:50 +03:00
Ilya Fedin
d349759618 Update cmake_helpers 2022-01-12 20:34:51 +03:00
Ilya Fedin
ac3e4fb42f Add missing openssl dependency for non-legacy tgcalls 2022-01-12 20:34:51 +03:00
Ilya Fedin
eccb01e5b5 Use ninja generator in snap 2022-01-12 20:34:51 +03:00
Ilya Fedin
7c8d10022f Use return to decrease indentation in cmake files 2022-01-12 20:34:51 +03:00
John Preston
f1244e19a1 Fix build for Windows. 2022-01-12 13:07:00 +03:00
John Preston
e17143dd8b Update lib_ui submodule. 2022-01-12 12:24:20 +03:00
23rd
39d5d3a1cf Moved some photo editor files to td_ui. 2022-01-12 11:54:25 +03:00
23rd
f8be5731a5 Moved out extracting of attached stickers from Scene to FileLoadTask. 2022-01-12 11:54:25 +03:00
23rd
ab248febcd Added ability to select text of question from polls.
Fixed #8713.
Fixed #17531.
2022-01-12 11:54:25 +03:00
23rd
d4afba3a24 Added ability to copy monospace text via click. 2022-01-12 11:54:25 +03:00
23rd
4ee9751feb Added ability to cancel selection in calendar box with Esc key. 2022-01-12 11:54:24 +03:00
23rd
46fb5ee1d2 Added Page Up / Down keys to calendar box. 2022-01-12 11:54:24 +03:00
23rd
749f837df5 Fixed Home and End keys in calendar box. 2022-01-12 11:54:24 +03:00
23rd
e11904e05b Removed TextParseRichText. 2022-01-12 11:54:24 +03:00
23rd
e1aa08b985 Removed text commands. 2022-01-12 11:54:24 +03:00
23rd
2af3770b29 Moved special text command for lang tags to td_lang. 2022-01-12 11:54:24 +03:00
23rd
74f9d0935b Removed text commands from skip blocks. 2022-01-12 11:54:24 +03:00
23rd
f9c50fdc06 Removed text commands from theme preview. 2022-01-12 11:54:24 +03:00
23rd
1fa825321d Removed text commands from poll box. 2022-01-12 11:54:24 +03:00
23rd
d9147562e5 Removed text commands from profile cover. 2022-01-12 11:54:24 +03:00
23rd
5b569718ec Removed text commands from main menu. 2022-01-12 11:54:24 +03:00
23rd
a5d4746202 Removed text commands from overview layout for voices. 2022-01-12 11:54:24 +03:00
23rd
50d150302d Removed text commands from web pages. 2022-01-12 11:54:24 +03:00
23rd
e451eb5126 Removed text commands from dialogs list. 2022-01-12 11:54:24 +03:00
23rd
a626364430 Removed text commands from connection box. 2022-01-12 11:54:24 +03:00
23rd
b55ed7214a Removed text commands from history item components. 2022-01-12 11:54:24 +03:00
23rd
d6801517bb Removed text commands from url auth box. 2022-01-12 11:54:24 +03:00
23rd
97dde7eb56 Removed text commands from Export::View::TopBar. 2022-01-12 11:54:24 +03:00
23rd
10df3dce7c Removed text commands from dialogs row. 2022-01-12 11:54:24 +03:00
23rd
889d7c0c15 Added undo and redo shortcuts to photo editor. 2022-01-12 11:54:24 +03:00
Ilya Fedin
799155279f Update kwayland and move its dependencies to ThirdParty to keep them in sync 2022-01-11 16:51:01 +03:00
Vitaly Zaitsev
10e7bd0d6e Updated XDG SPEC version to 1.5.
SingleMainWindow is a part of XDG SPEC version 1.5 and bogus on 1.0.

Co-authored-by: gasinvein <gasinvein@gmail.com>
Signed-off-by: Vitaly Zaitsev <vitaly@easycoding.org>
2022-01-11 10:37:44 +03:00
TheEvilSkeleton
0a99487091 Re-add X-GNOME-SingleWindow
`X-GNOME-SingleWindow` is best to be used for backward compatibility. Since `SingleMainWindow` was recently added, many Desktop Environments and Window Managers may not have it implemented yet.

This MR also moves `SingleMainWindow` before the `X-GNOME-*` keys, as requested in this comment: https://github.com/telegramdesktop/tdesktop/pull/23875#issuecomment-1008453759
2022-01-10 09:34:49 +03:00
Felipe Kinoshita
4965c19314 Change X-GNOME-SingleWindow key to SingleMainWindow
X-GNOME-SingleWindow was upstreamed to be an XDG spec with the name
"SingleMainWindow" in
https://gitlab.freedesktop.org/xdg/xdg-specs/-/merge_requests/53
2022-01-09 09:50:50 +03:00
Ilya Fedin
30810e95f4 Log when DE is unknown 2022-01-08 10:59:47 +03:00
Ilya Fedin
a3d84f69ea fixup! Use more sources for DE detection 2022-01-08 10:45:13 +03:00
Ilya Fedin
b3bb1a537c Use more sources for DE detection 2022-01-08 09:55:34 +03:00
Ilya Fedin
726aa3316d Rework DE detection
Variables can point to a mixed environment, make DE detection non-exclusive.
Remove unused methods.
2022-01-07 19:14:59 +03:00
John Preston
ba6c3eaf73 Add dummy Platform::Integration on Linux. 2022-01-06 15:44:02 +03:00
John Preston
ebe45f3fa1 Fix selecting in expanded down dropdown. 2022-01-06 15:42:32 +03:00
John Preston
3f0fed19d8 Fix build for macOS. 2022-01-06 15:41:17 +03:00
John Preston
609cab6e2f Fix build with Xcode. 2022-01-06 15:41:12 +03:00
John Preston
7b3cb0c3dd Allow non-colored and .tgs Lottie::Icon-s. 2022-01-05 15:14:39 +03:00
John Preston
43559fb6b7 Fix crash in history switching. 2022-01-05 15:14:39 +03:00
John Preston
8788692fb3 Fix crash in sending a reaction. 2022-01-05 15:14:39 +03:00
Ilya Fedin
072e346324 Move libdl/libpthread handling to common_options 2022-01-05 13:18:08 +03:00
23rd
99f65ab5ec Migrated dependencies in Github CI for Windows. 2022-01-05 10:48:06 +03:00
John Preston
fe7b120003 Fix possible crash in dictionaries download.
Fixes #17258.
2022-01-05 01:04:38 +03:00
John Preston
cb8f86bc8d Attach main views to correct HistoryInner-s. 2022-01-05 00:11:29 +03:00
John Preston
18e6e2da9e Open specific chat only in one window. 2022-01-04 19:36:33 +03:00
John Preston
54247cd11b Create dialogs widget only in the primary window. 2022-01-04 16:44:53 +03:00
John Preston
8b0725650d Move global event filter to Platform::Integration on Windows. 2022-01-04 15:29:40 +03:00
John Preston
20411be9bd Allow creating separate windows for peers. 2022-01-04 14:18:13 +03:00
github-actions[bot]
f4f36d85b9 Update copyright year to 2022. 2022-01-04 12:49:50 +04:00
John Preston
9f887237eb Remove a couple of unused lang keys. 2022-01-04 11:15:09 +03:00
GitHub Action
4e3f917a2c Update User-Agent for DNS to Chrome 96.0.4664.110. 2022-01-04 00:12:00 +03:00
John Preston
5c9c836857 Version 3.4.3: Fix build with GCC. 2022-01-04 00:01:56 +03:00
John Preston
31b7fe6ba0 Version 3.4.3.
- Bug fixes and other minor improvements.
2022-01-03 20:04:53 +03:00
John Preston
102c0a96ed Re-enable XWayland by default on GNOME.
Should fix #17457, fix #17468, fix #17476, fix #17477, fix #1747,
fix #17481, fix #17498.
2022-01-03 20:03:06 +03:00
John Preston
9a0be43ef5 Align reactions outside of the bubble. 2022-01-03 20:00:18 +03:00
John Preston
c1d948ef63 Reshuffle chat menus. 2022-01-03 18:16:19 +03:00
John Preston
9df229a230 Add pinned message icon.
Regression was introduced in 1af2cfe143.

Fixes #17489.
2022-01-03 18:15:36 +03:00
John Preston
a1c342c822 Leave only one style of the reaction button. 2022-01-03 15:30:17 +03:00
John Preston
c313cfb4ec Don't show empty context menu. 2022-01-03 14:47:05 +03:00
John Preston
8d4a658d0b Use mirrors for freedesktop.org repositories. 2022-01-03 14:14:26 +03:00
John Preston
86f53d3eff Fix crash after comments button destruction.
Regression was introduced in df66162bca.
2022-01-03 11:40:42 +03:00
John Preston
3cb89339c8 Version 3.4.2: Fix build for Mac App Store. 2021-12-31 23:40:36 +03:00
John Preston
ba98a8df32 Version 3.4.2: Fix build with GCC. 2021-12-31 23:32:53 +03:00
John Preston
f3faa52bc7 Version 3.4.2.
- Bug fixes and other minor improvements.
2021-12-31 18:03:25 +03:00
John Preston
3dac08d34f Move reaction button from fast share button. 2021-12-31 17:52:41 +03:00
John Preston
deba090cbd Allow smaller popup menus. 2021-12-31 17:52:17 +03:00
John Preston
5b01f9530b Fix reaction images loading. 2021-12-31 17:49:52 +03:00
John Preston
df66162bca Destroy comments button when switched off. 2021-12-31 17:31:03 +03:00
John Preston
d413080f83 Rebuild sets without restarting thumbnails. 2021-12-31 17:08:34 +03:00
John Preston
38ee57f852 Don't jump to top in StickersBox on stickersUpdated. 2021-12-31 16:50:43 +03:00
John Preston
c632316ad7 Fix updated sticker set thumbnail loading. 2021-12-31 16:40:01 +03:00
John Preston
bba7010e74 Show "View Message" button in sponsored. 2021-12-31 16:07:36 +03:00
John Preston
edf93b0031 Use different color for sponsored sender name. 2021-12-31 16:07:36 +03:00
John Preston
611be90880 Rewrite sponsored to use fake sender names. 2021-12-31 16:07:36 +03:00
John Preston
68886e1b61 Fix channel post views with replies counters. 2021-12-31 14:48:56 +03:00
John Preston
67319c1612 Version 3.4.1: Fix build with GCC. 2021-12-31 12:33:23 +03:00
John Preston
da8db0157f Version 3.4.1: Fix build without DBus. 2021-12-31 10:37:54 +03:00
John Preston
6188268afd Version 3.4.1.
- Bug fixes and other minor improvements.
2021-12-31 02:42:44 +03:00
John Preston
cd0db53bac For non-bubble messages reaction to the left of info. 2021-12-31 02:40:03 +03:00
John Preston
5bb90679a8 Attempt to fix a weird assertion violation. 2021-12-31 01:20:28 +03:00
John Preston
72df3a8f91 Don't show reaction button while selecting text. 2021-12-31 01:03:45 +03:00
John Preston
5fe2e649fb Attempt to fix a crash in reactions list view. 2021-12-31 00:59:29 +03:00
John Preston
9eba8ccc73 Always show reaction emoji in reactions view box. 2021-12-31 00:58:59 +03:00
John Preston
bb3c91aa44 Scale reaction images explicitly.
Fixes #17459.
2021-12-31 00:28:44 +03:00
John Preston
9f1268b6c8 Move kwayland-qt6 patch to kwayland build rule folder.
Fixes #17460.
2021-12-31 00:25:31 +03:00
John Preston
a6f1a1bd62 Fix bottom info with author signature.
Fixes #17464.
2021-12-30 23:57:12 +03:00
John Preston
1b2642b017 Fix spoilers with disabled animations.
Fixes #17458.
2021-12-30 23:38:28 +03:00
John Preston
e722645e7c Try to show the reaction button outside of the bubble. 2021-12-30 23:38:06 +03:00
John Preston
9486c266b5 Use context menu background for sticker reaction dropdown. 2021-12-30 23:36:52 +03:00
John Preston
dc21491099 Fix reactions icon in Manage Group / Channel. 2021-12-30 18:24:12 +03:00
John Preston
2f48bbd317 Version 3.4: Fix build with GCC. 2021-12-30 18:18:30 +03:00
John Preston
450f9ca91e Version 3.4: Update nimf submodule to clone fine on Windows. 2021-12-30 16:53:04 +03:00
John Preston
f86e2d98cc Version 3.4: Show mini-profiles from reactions overview. 2021-12-30 16:48:52 +03:00
John Preston
0cf85be86b Version 3.4.
- Send reactions to messages.
- Group and Channel admins can enable reactions in their chat
via '...' menu > Manage > Reactions.
- Select text when typing and choose 'Formatting > Spoiler' in
the context menu to hide some or all of the contents of a message.
- Click on the spoiler in chat to reveal its hidden text.
- Spoiler formatting hides text in chat,
as well as in the chat list and notifications.
2021-12-30 16:25:43 +03:00
John Preston
a15ef8bbc2 Check noforwards of specific messages in shared media. 2021-12-30 16:15:38 +03:00
John Preston
ecca60afe4 Some spoiler improvements. 2021-12-30 16:08:15 +03:00
John Preston
1ab0f840f3 Use reactions icon in context menu if more than 1 reaction type. 2021-12-30 16:08:15 +03:00
John Preston
3623fb1f9a Disable saveAs in media viewer if restricted. 2021-12-30 14:58:34 +03:00
John Preston
4cbfcc8dbc Disable reaction button on Call messages. 2021-12-30 14:33:11 +03:00
John Preston
90821428d3 Fix quick reaction button on Retina screens. 2021-12-30 14:33:11 +03:00
John Preston
c5468a1111 Fix spoilers in some cases. 2021-12-30 13:37:56 +03:00
John Preston
505ef04134 Show reaction button only when the cursor is over the message. 2021-12-30 13:37:39 +03:00
John Preston
f70c2adbdd Fix menu icons in proxy settings. 2021-12-30 13:37:17 +03:00
437 changed files with 13644 additions and 8686 deletions

View File

@@ -91,6 +91,11 @@ jobs:
run: |
./$REPO_NAME/Telegram/build/prepare/mac.sh skip-release silent
- name: Free up some disk space.
run: |
cd Libraries
find . -iname "*.dir" -exec rm -rf {} || true \;
- name: Telegram Desktop build.
if: env.ONLY_CACHE == 'false'
run: |

View File

@@ -44,385 +44,84 @@ jobs:
windows:
name: Windows
runs-on: windows-latest
runs-on: windows-2022
strategy:
matrix:
defines:
- ""
arch: [Win32, x64]
env:
SDK: "10.0.18362.0"
GIT: "https://github.com"
QT_VER: "5.15.2"
OPENSSL_VER: "1_1_1"
UPLOAD_ARTIFACT: "false"
ONLY_CACHE: "false"
MANUAL_CACHING: "0"
PREPARE_PATH: "Telegram/build/prepare/prepare.py"
AUTO_CACHING: "1"
defaults:
run:
shell: cmd
working-directory: Libraries
working-directory: ${{ github.workspace }}
steps:
- name: Prepare directories.
run: |
mkdir %userprofile%\TBuild\Libraries
echo TBUILD=%userprofile%\TBuild>>%GITHUB_ENV%
- name: Get repository name.
shell: bash
working-directory: ${{ github.workspace }}
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
- uses: ilammy/msvc-dev-cmd@v1.9.0
name: x86 Native Tools Command Prompt.
- uses: ilammy/msvc-dev-cmd@v1.10.0
name: Native Tools Command Prompt.
with:
arch: win32
- name: Set up environment paths.
shell: bash
working-directory: ${{ github.workspace }}
run: |
echo "C:\\Strawberry\\perl\\bin\\" >> $GITHUB_PATH
echo "C:\\Program Files\\NASM\\" >> $GITHUB_PATH
echo "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\VC\\Auxiliary\\Build\\" >> $GITHUB_PATH
mkdir Libraries && cd Libraries
echo "Convert unix path to win path."
p=`pwd | sed 's#^/[d]#d:#g' |sed 's#/#\\\\#g'`
echo "LibrariesPath=$p" >> $GITHUB_ENV
echo "QT=${QT_VER//./_}" >> $GITHUB_ENV
- name: Save msbuild version.
run: |
call vcvars32.bat
msbuild -version > CACHE_KEY.txt
arch: ${{ matrix.arch }}
- name: Clone.
uses: actions/checkout@v2
uses: LebedevRI/checkout@issue197
with:
submodules: recursive
path: ${{ env.REPO_NAME }}
- name: Generate cache key.
shell: bash
working-directory: ${{ github.workspace }}
run: |
curl -o $LibrariesPath/tg_owt-version.json https://api.github.com/repos/desktop-app/tg_owt/git/refs/heads/master
curl -o $LibrariesPath/tg_angle-version.json https://api.github.com/repos/desktop-app/tg_angle/git/refs/heads/master
echo $MANUAL_CACHING >> CACHE_KEY.txt
if [ "$AUTO_CACHING" == "1" ]; then
thisFile=$REPO_NAME/.github/workflows/win.yml
echo `md5sum $thisFile | awk '{ print $1 }'` >> CACHE_KEY.txt
fi
echo "CACHE_KEY=`md5sum CACHE_KEY.txt | awk '{ print $1 }'`" >> $GITHUB_ENV
path: ${{ env.TBUILD }}\${{ env.REPO_NAME }}
- name: Choco installs.
run: |
choco install --allow-empty-checksums --no-progress -y yasm
choco install --no-progress -y nasm jom ninja
python -m pip install pywin32
choco install --no-progress -y nasm strawberryperl yasm jom ninja
py -m pip install pywin32
- name: Install msys64.
run: |
mkdir %TBUILD%\ThirdParty
xcopy /E /I C:\msys64 %TBUILD%\ThirdParty\msys64
- name: Set up environment paths.
shell: bash
run: |
echo "C:\\Strawberry\\perl\\bin\\" >> $GITHUB_PATH
echo "C:\\Program Files\\NASM\\" >> $GITHUB_PATH
echo "C:\\ProgramData\\chocolatey\\lib\\ninja\\tools\\" >> $GITHUB_PATH
echo "Configurate git for cherry-picks."
git config --global user.email "you@example.com"
git config --global user.name "Sample"
- name: NuGet sources.
run: |
nuget sources Disable -Name "Microsoft Visual Studio Offline Packages"
nuget sources Add -Source https://api.nuget.org/v3/index.json & exit 0
- name: Patches.
shell: bash
working-directory: ${{ github.workspace }}
run: |
echo "Find necessary commit from doc."
checkoutCommit=$(grep -A 1 "cd patches" $REPO_NAME/$PREPARE_PATH | sed -n 2p)
cd $LibrariesPath
git clone $GIT/desktop-app/patches.git
cd patches
eval $checkoutCommit
- name: LZMA.
run: |
git clone %GIT%/telegramdesktop/lzma.git
cd lzma
cd C\Util\LzmaLib
msbuild -m LzmaLib.sln /property:Configuration=Debug
- name: OpenSSL cache.
id: cache-openssl
- name: Libraries cache.
id: cache-libs
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/openssl
key: ${{ runner.OS }}-${{ env.CACHE_KEY }}-${{ env.OPENSSL_VER }}
- name: OpenSSL.
if: steps.cache-openssl.outputs.cache-hit != 'true'
run: |
git clone -b OpenSSL_%OPENSSL_VER%-stable %GIT%/openssl/openssl.git
cd openssl
perl Configure no-shared no-tests debug-VC-WIN32
nmake
mkdir out.dbg
move libcrypto.lib out.dbg
move libssl.lib out.dbg
move ossl_static.pdb out.dbg\ossl_static
nmake clean
move out.dbg\ossl_static out.dbg\ossl_static.pdb
perl Configure no-shared no-tests VC-WIN32
nmake
mkdir out
move libcrypto.lib out
move libssl.lib out
move ossl_static.pdb out
path: ${{ env.TBUILD }}/Libraries
key: ${{ runner.OS }}-libs
rmdir /S /Q test
rmdir /S /Q .git
- name: Zlib.
run: |
git clone %GIT%/telegramdesktop/zlib.git
cd zlib
git checkout tdesktop
cd contrib\vstudio\vc14
msbuild -m zlibstat.vcxproj /property:Configuration=Debug
- name: MozJPEG.
shell: cmd
run: |
git clone -b v4.0.3 %GIT%/mozilla/mozjpeg.git
cd mozjpeg
cmake . ^
-G "Visual Studio 16 2019" ^
-A Win32 ^
-DWITH_JPEG8=ON ^
-DPNG_SUPPORTED=OFF
cmake --build . --config Debug
- name: OpenAL Soft cache.
id: cache-openal
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/openal-soft
key: ${{ runner.OS }}-openal-soft-${{ env.CACHE_KEY }}
- name: OpenAL Soft.
if: steps.cache-openal.outputs.cache-hit != 'true'
run: |
git clone -b openal-soft-1.21.0 --depth=1 %GIT%/kcat/openal-soft.git
cd openal-soft\build
cmake .. ^
-G "Visual Studio 16 2019" ^
-A Win32 ^
-D LIBTYPE:STRING=STATIC ^
-D FORCE_STATIC_VCRT=ON ^
-D ALSOFT_BACKEND_DSOUND=OFF
msbuild -m OpenAL.vcxproj /property:Configuration=Debug
- name: Breakpad cache.
id: cache-breakpad
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/breakpad
key: ${{ runner.OS }}-breakpad-${{ env.CACHE_KEY }}-${{ hashFiles('**/breakpad.diff') }}
- name: Breakpad.
- name: Libraries.
env:
GYP_MSVS_OVERRIDE_PATH: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\'
GYP_MSVS_VERSION: 2019
if: steps.cache-breakpad.outputs.cache-hit != 'true'
GYP_MSVS_OVERRIDE_PATH: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\'
GYP_MSVS_VERSION: 2022
run: |
git clone https://chromium.googlesource.com/external/gyp
cd gyp
SET PATH=%cd%;%PATH%
git checkout d6c5dd51dc
git apply ../patches/gyp.diff
cd %LibrariesPath%
git clone https://chromium.googlesource.com/breakpad/breakpad
cd breakpad
git checkout dfcb7b6799
git apply ../patches/breakpad.diff
cd src
git clone -b release-1.11.0 %GIT%/google/googletest testing
cd client\windows
call gyp --no-circular-check breakpad_client.gyp --format=ninja
cd ..\..
ninja -C out/Debug common crash_generation_client exception_handler
ninja -C out/Release common crash_generation_client exception_handler
cd tools\windows\dump_syms
call gyp dump_syms.gyp --format=ninja
cd ..\..\..
ninja -C out/Release dump_syms
- name: Opus cache.
id: cache-opus
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/opus
key: ${{ runner.OS }}-opus-${{ env.CACHE_KEY }}
- name: Opus.
if: steps.cache-opus.outputs.cache-hit != 'true'
run: |
git clone -b v1.3.1 %GIT%/xiph/opus.git
cd opus
git cherry-pick 927de8453c
cmake -B out . ^
-A Win32 ^
-DCMAKE_INSTALL_PREFIX=%LibrariesPath%/local/opus ^
-DCMAKE_C_FLAGS_DEBUG="/MTd /Zi /Ob0 /Od /RTC1" ^
-DCMAKE_C_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG"
cmake --build out --config Debug
cmake --build out --config Release
cmake --install out --config Release
- name: Rnnoise.
run: |
git clone %GIT%/desktop-app/rnnoise.git
mkdir rnnoise\out
cd rnnoise\out
cmake -A Win32 ..
cmake --build . --config Debug
- name: FFmpeg cache.
id: cache-ffmpeg
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/ffmpeg
key: ${{ runner.OS }}-ffmpeg-${{ env.CACHE_KEY }}-2-${{ hashFiles('**/build_ffmpeg_win.sh') }}
- name: FFmpeg.
if: steps.cache-ffmpeg.outputs.cache-hit != 'true'
run: |
choco install --no-progress -y msys2
git clone %GIT%/FFmpeg/FFmpeg.git ffmpeg
cd ffmpeg
git checkout release/4.4
set CHERE_INVOKING=enabled_from_arguments
set MSYS2_PATH_TYPE=inherit
call c:\tools\msys64\usr\bin\bash --login ../patches/build_ffmpeg_win.sh
rmdir /S /Q .git
- name: Angle cache.
id: cache-angle
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/tg_angle
key: ${{ runner.OS }}-angle-${{ env.CACHE_KEY }}-${{ hashFiles('**/tg_angle-version.json') }}
- name: Angle.
if: steps.cache-angle.outputs.cache-hit != 'true'
run: |
git clone --recursive %GIT%/desktop-app/tg_angle.git
mkdir tg_angle\out\Debug
cd tg_angle\out\Debug
cmake -G Ninja ^
-DCMAKE_BUILD_TYPE=Debug ^
-DTG_ANGLE_SPECIAL_TARGET=win64 ^
-DTG_ANGLE_ZLIB_INCLUDE_PATH=%cd%/../../../zlib ../..
ninja
:: Cleanup.
cd %LibrariesPath%\tg_angle
move out\Debug\tg_angle.lib tg_angle.lib
rmdir /S /Q out
mkdir out\Debug
move tg_angle.lib out\Debug\tg_angle.lib
- name: Qt 5.15.2 cache.
id: cache-qt
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/Qt-${{ env.QT_VER }}
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_5_15_2/*') }}
- name: Configure Qt 5.15.2.
if: steps.cache-qt.outputs.cache-hit != 'true'
run: |
git clone git://code.qt.io/qt/qt5.git qt_%QT%
cd qt_%QT%
perl init-repository --module-subset=qtbase,qtimageformats,qtsvg
git checkout v%QT_VER%
git submodule update qtbase
git submodule update qtimageformats
git submodule update qtsvg
cd qtbase
for /r %%i in (..\..\patches\qtbase_%QT%\*) do git apply %%i
cd ..
SET SSL=%LibrariesPath%\openssl
SET SSL_LIBS=libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib
SET ANGLE=%LibrariesPath%\tg_angle
SET ANGLE_LIBS=d3d9.lib dxgi.lib dxguid.lib
SET ZLIB=%LibrariesPath%\zlib\contrib\vstudio\vc14\x86
configure ^
-prefix "%LibrariesPath%\Qt-%QT_VER%" ^
-debug ^
-force-debug-info ^
-opensource ^
-confirm-license ^
-static ^
-static-runtime ^
-opengl es2 -no-angle ^
-I "%ANGLE%\include" ^
-D "GL_APICALL=" ^
QMAKE_LIBS_OPENGL_ES2_DEBUG="%ANGLE%\out\Debug\tg_angle.lib %ZLIB%\ZlibStatDebug\zlibstat.lib %ANGLE_LIBS%" ^
QMAKE_LIBS_OPENGL_ES2_RELEASE="%ANGLE%\out\Release\tg_angle.lib %ZLIB%\ZlibStatReleaseWithoutAsm\zlibstat.lib %ANGLE_LIBS%" ^
-egl ^
-D "EGLAPI=" ^
-D "DESKTOP_APP_QT_STATIC_ANGLE=" ^
QMAKE_LIBS_EGL_DEBUG="%ANGLE%\out\Debug\tg_angle.lib %ZLIB%\ZlibStatDebug\zlibstat.lib %ANGLE_LIBS% Gdi32.lib User32.lib" ^
QMAKE_LIBS_EGL_RELEASE="%ANGLE%\out\Release\tg_angle.lib %ZLIB%\ZlibStatReleaseWithoutAsm\zlibstat.lib %ANGLE_LIBS% Gdi32.lib User32.lib" ^
-openssl-linked ^
-I "%SSL%\include" ^
OPENSSL_LIBS_DEBUG="%SSL%\out.dbg\libssl.lib %SSL%\out.dbg\%SSL_LIBS%" ^
OPENSSL_LIBS_RELEASE="%SSL%\out\libssl.lib %SSL%\out\%SSL_LIBS%" ^
-I "%LibrariesPath%\mozjpeg" ^
LIBJPEG_LIBS_DEBUG="%LibrariesPath%\mozjpeg\Debug\jpeg-static.lib" ^
LIBJPEG_LIBS_RELEASE="%LibrariesPath%\mozjpeg\Release\jpeg-static.lib" ^
-mp ^
-nomake examples ^
-nomake tests ^
-platform win32-msvc
- name: Qt 5.15.2 build.
if: steps.cache-qt.outputs.cache-hit != 'true'
run: |
cd qt_%QT%
jom -j%NUMBER_OF_PROCESSORS%
jom -j%NUMBER_OF_PROCESSORS% install
cd ..
rmdir /S /Q qt_%QT%
- name: WebRTC cache.
id: cache-webrtc
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/tg_owt
key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}-${{ hashFiles('**/tg_owt-version.json') }}
- name: WebRTC.
if: steps.cache-webrtc.outputs.cache-hit != 'true'
run: |
git clone --recursive %GIT%/desktop-app/tg_owt.git
mkdir tg_owt\out\Debug
cd tg_owt\out\Debug
cmake -G Ninja ^
-DCMAKE_BUILD_TYPE=Debug ^
-DTG_OWT_SPECIAL_TARGET=win ^
-DTG_OWT_BUILD_AUDIO_BACKENDS=OFF ^
-DTG_OWT_LIBJPEG_INCLUDE_PATH=%cd%/../../../mozjpeg ^
-DTG_OWT_OPENSSL_INCLUDE_PATH=%cd%/../../../openssl/include ^
-DTG_OWT_OPUS_INCLUDE_PATH=%cd%/../../../opus/include ^
-DTG_OWT_FFMPEG_INCLUDE_PATH=%cd%/../../../ffmpeg ^
../..
ninja
:: Cleanup.
cd %LibrariesPath%\tg_owt
move out\Debug\tg_owt.lib tg_owt.lib
rmdir /S /Q out
mkdir out\Debug
move tg_owt.lib out\Debug\tg_owt.lib
C:
cd %TBUILD%
%REPO_NAME%/Telegram/build/prepare/win.bat skip-release silent
- name: Read defines.
shell: bash
@@ -438,16 +137,21 @@ jobs:
echo "TDESKTOP_BUILD_DEFINE=$DEFINE" >> $GITHUB_ENV
- name: Free up some disk space.
working-directory: ${{ github.workspace }}
run: del /S *.pdb
run: |
C:
cd %TBUILD%
del /S Libraries\*.pdb
del /S Libraries\*.pch
del /S Libraries\*.obj
- name: Telegram Desktop build.
if: env.ONLY_CACHE == 'false'
working-directory: ${{ github.workspace }}
run: |
cd %REPO_NAME%\Telegram
C:
cd %TBUILD%\%REPO_NAME%\Telegram
call configure.bat ^
${{ matrix.arch }} ^
-D TDESKTOP_API_TEST=ON ^
-D DESKTOP_APP_USE_PACKAGED=OFF ^
-D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF ^
@@ -455,20 +159,17 @@ jobs:
%TDESKTOP_BUILD_DEFINE% ^
-DCMAKE_SYSTEM_VERSION=%SDK%
call vcvars32.bat
cd ..\out
msbuild -m Telegram.sln /nologo /p:Configuration=Debug,Platform=Win32
msbuild -m Telegram.sln /p:Configuration=Debug,Platform=${{ matrix.arch }},DebugSymbols=false,DebugType=none
- name: Move artifact.
if: env.UPLOAD_ARTIFACT == 'true'
working-directory: ${{ github.workspace }}
run: |
cd %REPO_NAME%\out\Debug
mkdir artifact
move Telegram.exe artifact/
move %TBUILD%\%REPO_NAME%\out\Debug\Telegram.exe artifact/
- uses: actions/upload-artifact@master
name: Upload artifact.
if: env.UPLOAD_ARTIFACT == 'true'
with:
name: ${{ env.ARTIFACT_NAME }}
path: ${{ env.REPO_NAME }}\out\Debug\artifact\
path: artifact\

12
.gitmodules vendored
View File

@@ -52,9 +52,6 @@
[submodule "Telegram/lib_qr"]
path = Telegram/lib_qr
url = https://github.com/desktop-app/lib_qr.git
[submodule "Telegram/ThirdParty/libdbusmenu-qt"]
path = Telegram/ThirdParty/libdbusmenu-qt
url = https://github.com/desktop-app/libdbusmenu-qt.git
[submodule "Telegram/ThirdParty/hunspell"]
path = Telegram/ThirdParty/hunspell
url = https://github.com/hunspell/hunspell
@@ -94,3 +91,12 @@
[submodule "Telegram/ThirdParty/dispatch"]
path = Telegram/ThirdParty/dispatch
url = https://github.com/apple/swift-corelibs-libdispatch
[submodule "Telegram/ThirdParty/extra-cmake-modules"]
path = Telegram/ThirdParty/extra-cmake-modules
url = https://github.com/KDE/extra-cmake-modules.git
[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

2
LEGAL
View File

@@ -1,7 +1,7 @@
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
Copyright (c) 2014-2021 The Telegram Desktop Authors.
Copyright (c) 2014-2022 The Telegram Desktop Authors.
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@@ -147,6 +147,8 @@ PRIVATE
api/api_text_entities.h
api/api_toggling_media.cpp
api/api_toggling_media.h
api/api_unread_things.cpp
api/api_unread_things.h
api/api_updates.cpp
api/api_updates.h
api/api_user_privacy.cpp
@@ -525,33 +527,16 @@ PRIVATE
editor/controllers/controllers.h
editor/controllers/stickers_panel_controller.cpp
editor/controllers/stickers_panel_controller.h
editor/controllers/undo_controller.cpp
editor/controllers/undo_controller.h
editor/editor_crop.cpp
editor/editor_crop.h
editor/editor_paint.cpp
editor/editor_paint.h
editor/photo_editor.cpp
editor/photo_editor.h
editor/photo_editor_common.cpp
editor/photo_editor_common.h
editor/photo_editor_content.cpp
editor/photo_editor_content.h
editor/photo_editor_controls.cpp
editor/photo_editor_controls.h
editor/photo_editor_inner_common.h
editor/photo_editor_layer_widget.cpp
editor/photo_editor_layer_widget.h
editor/scene/scene.cpp
editor/scene/scene.h
editor/scene/scene_item_base.cpp
editor/scene/scene_item_base.h
editor/scene/scene_item_canvas.cpp
editor/scene/scene_item_canvas.h
editor/scene/scene_item_image.cpp
editor/scene/scene_item_image.h
editor/scene/scene_item_line.cpp
editor/scene/scene_item_line.h
editor/scene/scene_item_sticker.cpp
editor/scene/scene_item_sticker.h
export/export_manager.cpp
@@ -655,6 +640,8 @@ PRIVATE
history/view/history_view_pinned_section.h
history/view/history_view_pinned_tracker.cpp
history/view/history_view_pinned_tracker.h
history/view/history_view_react_animation.cpp
history/view/history_view_react_animation.h
history/view/history_view_react_button.cpp
history/view/history_view_react_button.h
history/view/history_view_reactions.cpp
@@ -701,6 +688,8 @@ PRIVATE
history/history_message.h
history/history_service.cpp
history/history_service.h
history/history_unread_things.cpp
history/history_unread_things.h
history/history_widget.cpp
history/history_widget.h
info/info_content_widget.cpp
@@ -948,6 +937,8 @@ PRIVATE
platform/linux/file_utilities_linux.h
platform/linux/launcher_linux.cpp
platform/linux/launcher_linux.h
platform/linux/integration_linux.cpp
platform/linux/integration_linux.h
platform/linux/main_window_linux.cpp
platform/linux/main_window_linux.h
platform/linux/notifications_manager_linux_dummy.cpp
@@ -959,6 +950,8 @@ PRIVATE
platform/mac/file_utilities_mac.h
platform/mac/launcher_mac.mm
platform/mac/launcher_mac.h
platform/mac/integration_mac.mm
platform/mac/integration_mac.h
platform/mac/mac_iconv_helper.c
platform/mac/main_window_mac.mm
platform/mac/main_window_mac.h
@@ -993,6 +986,8 @@ PRIVATE
platform/win/file_utilities_win.h
platform/win/launcher_win.cpp
platform/win/launcher_win.h
platform/win/integration_win.cpp
platform/win/integration_win.h
platform/win/main_window_win.cpp
platform/win/main_window_win.h
platform/win/notifications_manager_win.cpp
@@ -1003,8 +998,6 @@ PRIVATE
platform/win/windows_app_user_model_id.h
platform/win/windows_dlls.cpp
platform/win/windows_dlls.h
platform/win/windows_event_filter.cpp
platform/win/windows_event_filter.h
platform/win/windows_autostart_task.cpp
platform/win/windows_autostart_task.h
platform/win/windows_toast_activator.cpp
@@ -1012,6 +1005,8 @@ PRIVATE
platform/platform_audio.h
platform/platform_file_utilities.h
platform/platform_launcher.h
platform/platform_integration.cpp
platform/platform_integration.h
platform/platform_main_window.h
platform/platform_notifications_manager.h
platform/platform_specific.h
@@ -1036,6 +1031,8 @@ PRIVATE
settings/settings_codes.h
settings/settings_common.cpp
settings/settings_common.h
settings/settings_experimental.cpp
settings/settings_experimental.h
settings/settings_folders.cpp
settings/settings_folders.h
settings/settings_information.cpp
@@ -1202,8 +1199,6 @@ PRIVATE
window/themes/window_themes_generate_name.h
apiwrap.cpp
apiwrap.h
app.cpp
app.h
config.h
facades.cpp
facades.h
@@ -1343,8 +1338,6 @@ else()
if (NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
target_link_libraries(Telegram
PRIVATE
desktop-app::external_statusnotifieritem
desktop-app::external_dbusmenu_qt
desktop-app::external_glibmm
desktop-app::external_glib
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 616 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 889 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 814 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 453 B

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 973 B

After

Width:  |  Height:  |  Size: 819 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 946 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 B

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 995 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 823 B

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -348,6 +348,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_notification_sample" = "This is a sample notification";
"lng_notification_reminder" = "Reminder";
"lng_reaction_text" = "{reaction} to your «{text}»";
"lng_reaction_notext" = "{reaction} to your message";
"lng_reaction_photo" = "{reaction} to your photo";
"lng_reaction_video" = "{reaction} to your video";
"lng_reaction_video_message" = "{reaction} to your video message";
"lng_reaction_document" = "{reaction} to your file";
"lng_reaction_sticker" = "{reaction} to your {emoji}sticker";
"lng_reaction_voice_message" = "{reaction} to your voice message";
"lng_reaction_contact" = "{reaction} to your contact {name}";
"lng_reaction_location" = "{reaction} to your map";
"lng_reaction_live_location" = "{reaction} to your live location";
"lng_reaction_poll" = "{reaction} to your poll {title}";
"lng_reaction_quiz" = "{reaction} to your quiz {title}";
"lng_reaction_game" = "{reaction} to your game";
"lng_reaction_invoice" = "{reaction} to your invoice";
"lng_reaction_gif" = "{reaction} to your GIF";
"lng_settings_section_general" = "General";
"lng_settings_change_lang" = "Change language";
"lng_languages" = "Languages";
@@ -372,9 +389,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_auto_start_disabled_uwp" = "Starting with the system was disabled in Windows Settings.\n\nPlease enable Telegram Desktop in the Startup Apps Settings.";
"lng_settings_open_system_settings" = "Open Settings";
"lng_settings_add_sendto" = "Place Telegram in \"Send to\" menu";
"lng_settings_mac_warn_before_quit" = "Show warning before quitting with {text}";
"lng_settings_section_scale" = "Interface Scale";
"lng_settings_scale_auto" = "Auto ({cur})";
"lng_settings_experimental" = "Experimental settings";
"lng_settings_experimental_about" = "Warning! Those are experimental settings. Some may not work. Others may break the app. Any of them may disappear in the next version without a trace. Use at your own risk.";
"lng_settings_section_chat_settings" = "Chat Settings";
"lng_settings_replace_emojis" = "Replace emoji";
"lng_settings_suggest_emoji" = "Suggest emoji replacements";
@@ -440,7 +461,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_phone_label" = "Phone number";
"lng_settings_username_add" = "Add username";
"lng_settings_close_sure" = "Are you sure you want to close this page? You didn't save your changes.";
//"lng_settings_peer_to_peer" = "Peer-to-Peer";
"lng_settings_peer_to_peer_about" = "Disabling peer-to-peer will relay all calls through Telegram servers to avoid revealing your IP address, but may slightly decrease audio quality.";
"lng_settings_advanced" = "Advanced";
"lng_settings_stickers_emoji" = "Stickers and emoji";
@@ -917,8 +937,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_files_header" = "Files";
"lng_profile_audios#one" = "{count} voice message";
"lng_profile_audios#other" = "{count} voice messages";
//"lng_profile_rounds#one" = "{count} video message";
//"lng_profile_rounds#other" = "{count} video messages";
"lng_profile_audios_header" = "Voice messages";
"lng_profile_shared_links#one" = "{count} shared link";
"lng_profile_shared_links#other" = "{count} shared links";
@@ -978,8 +996,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_media_selected_file#other" = "{count} Files";
"lng_media_selected_audio#one" = "{count} Voice message";
"lng_media_selected_audio#other" = "{count} Voice messages";
//"lng_media_selected_round#one" = "{count} Video message";
//"lng_media_selected_round#other" = "{count} Video messages";
"lng_media_selected_link#one" = "{count} Shared link";
"lng_media_selected_link#other" = "{count} Shared links";
"lng_media_photo_empty" = "No photos here yet";
@@ -1259,10 +1275,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_you_proximity_reached" = "You are now within {distance} from {user}";
"lng_action_you_theme_changed" = "You changed chat theme to {emoji}";
"lng_action_theme_changed" = "{from} changed chat theme to {emoji}";
"lng_action_theme_changed_channel" = "Channel theme changed to {emoji}";
"lng_action_you_theme_disabled" = "You disabled chat theme";
"lng_action_theme_disabled" = "{from} disabled chat theme";
"lng_action_theme_disabled_channel" = "Channel theme disabled";
"lng_action_proximity_distance_m#one" = "{count} meter";
"lng_action_proximity_distance_m#other" = "{count} metres";
"lng_action_proximity_distance_km#one" = "{count} km";
@@ -1729,7 +1743,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_send_message" = "Send message";
"lng_context_view_group" = "View group info";
"lng_context_view_channel" = "View channel info";
//"lng_context_view_feed_info" = "View feed info";
"lng_context_hide_psa" = "Hide this announcement";
"lng_context_pin_to_top" = "Pin to top";
"lng_context_unpin_from_top" = "Unpin from top";
@@ -1739,12 +1752,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_mark_read_all" = "Mark all chats as read";
"lng_context_mark_read_all_sure" = "Are you sure you want to mark all chats as read?";
"lng_context_mark_read_mentions_all" = "Mark all mentions as read";
"lng_context_mark_read_reactions_all" = "Read all reactions";
"lng_context_archive_expand" = "Expand";
"lng_context_archive_collapse" = "Collapse";
"lng_context_archive_to_menu" = "Move to main menu";
"lng_context_archive_to_list" = "Move to chats list";
"lng_context_archive_to_menu_info" = "Archive moved to the main menu!\nYou can return it from the context menu of the archive button.";
"lng_context_mute" = "Mute notifications";
"lng_context_unmute" = "Unmute";
"lng_context_promote_admin" = "Promote to admin";
"lng_context_edit_permissions" = "Edit permissions";
"lng_context_restrict_user" = "Restrict user";
@@ -1807,6 +1824,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_context_seen_reacted#other" = "{count} Reacted";
"lng_context_seen_reacted_none" = "Nobody Reacted";
"lng_context_seen_reacted_all" = "Show All Reactions";
"lng_context_set_as_quick" = "Set As Quick";
"lng_send_image_empty" = "Could not send an empty file: {name}";
"lng_send_image_too_large" = "Could not send a file, because it is larger than 1500 MB: {name}";
@@ -1829,7 +1847,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_forward_share_contact" = "Share contact to {recipient}?";
"lng_forward_share_cant" = "Sorry, no way to share contact here :(";
"lng_forward_send_files_cant" = "Sorry, no way to send media here :(";
"lng_forward_send" = "Send";
"lng_forward_messages#one" = "{count} forwarded message";
@@ -1910,6 +1927,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_box_delete" = "Delete";
"lng_box_leave" = "Leave";
"lng_upload_sure_stop" = "Are you sure you want to stop uploading your files?\n\nIf you do, you'll need to start over.";
"lng_upload_show_file" = "Show file";
"lng_about_version" = "version {version}";
"lng_about_text1" = "Official free messaging app based on {api_link}\nfor speed and security.";
"lng_about_text1_api" = "Telegram API";
@@ -1977,6 +1997,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_formatting_link_url" = "URL";
"lng_formatting_link_create" = "Create";
"lng_text_copied" = "Text copied to clipboard.";
"lng_spellchecker_submenu" = "Spelling";
"lng_spellchecker_add" = "Add to Dictionary";
"lng_spellchecker_remove" = "Remove from Dictionary";
@@ -3044,6 +3066,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_mac_menu_preferences" = "Preferences...";
"lng_mac_menu_quit_telegram" = "Quit {telegram}";
"lng_mac_menu_about_telegram" = "About {telegram}";
//"lng_mac_menu_warn_before_quit" = "Warn Before Quitting ({text})";
"lng_mac_menu_file" = "File";
"lng_mac_menu_logout" = "Log Out";
"lng_mac_menu_edit" = "Edit";
@@ -3070,4 +3093,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_mac_touchbar_favorite_stickers" = "Favorite stickers";
"lng_mac_hold_to_quit" = "Hold {text} to Quit";
// Keys finished

View File

@@ -190,7 +190,7 @@ messageActionGroupCallScheduled#b3a07661 call:InputGroupCall schedule_date:int =
messageActionSetChatTheme#aa786345 emoticon:string = MessageAction;
messageActionChatJoinedByRequest#ebbca3cb = MessageAction;
dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
dialog#a8edd0f5 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
photoEmpty#2331b22d id:long = Photo;
@@ -566,7 +566,7 @@ inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
stickerSet#d7df217a flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int count:int hash:int = StickerSet;
stickerSet#d7df217a flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true gifs:flags.6?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int count:int hash:int = StickerSet;
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
@@ -1307,17 +1307,20 @@ auth.loggedOut#c3a2835f flags:# future_auth_token:flags.0?bytes = auth.LoggedOut
reactionCount#6fb250d1 flags:# chosen:flags.0?true reaction:string count:int = ReactionCount;
messageReactions#87b6e36 flags:# min:flags.0?true can_see_list:flags.2?true results:Vector<ReactionCount> recent_reactons:flags.1?Vector<MessageUserReaction> = MessageReactions;
messageReactions#4f2b9479 flags:# min:flags.0?true can_see_list:flags.2?true results:Vector<ReactionCount> recent_reactions:flags.1?Vector<MessagePeerReaction> = MessageReactions;
messageUserReaction#932844fa user_id:long reaction:string = MessageUserReaction;
messages.messageReactionsList#31bd492d flags:# count:int reactions:Vector<MessagePeerReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = messages.MessageReactionsList;
messages.messageReactionsList#a366923c flags:# count:int reactions:Vector<MessageUserReaction> users:Vector<User> next_offset:flags.0?string = messages.MessageReactionsList;
availableReaction#21d7c4b flags:# inactive:flags.0?true reaction:string title:string static_icon:Document appear_animation:Document select_animation:Document activate_animation:Document effect_animation:Document = AvailableReaction;
availableReaction#c077ec01 flags:# inactive:flags.0?true reaction:string title:string static_icon:Document appear_animation:Document select_animation:Document activate_animation:Document effect_animation:Document around_animation:flags.1?Document center_icon:flags.1?Document = AvailableReaction;
messages.availableReactionsNotModified#9f071957 = messages.AvailableReactions;
messages.availableReactions#768e3aad hash:int reactions:Vector<AvailableReaction> = messages.AvailableReactions;
messages.translateNoResult#67ca4737 = messages.TranslatedText;
messages.translateResultText#a214f7d0 text:string = messages.TranslatedText;
messagePeerReaction#51b67eff flags:# big:flags.0?true unread:flags.1?true peer_id:Peer reaction:string = MessagePeerReaction;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -1596,12 +1599,15 @@ messages.hideChatJoinRequest#7fe7e815 flags:# approved:flags.0?true peer:InputPe
messages.hideAllChatJoinRequests#e085f4ea flags:# approved:flags.0?true peer:InputPeer link:flags.1?string = Updates;
messages.toggleNoForwards#b11eafa2 peer:InputPeer enabled:Bool = Updates;
messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool;
messages.sendReaction#25690ce4 flags:# peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
messages.sendReaction#25690ce4 flags:# big:flags.1?true peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
messages.getMessagesReactions#8bba90e6 peer:InputPeer id:Vector<int> = Updates;
messages.getMessageReactionsList#e0ee6b77 flags:# peer:InputPeer id:int reaction:flags.0?string offset:flags.1?string limit:int = messages.MessageReactionsList;
messages.setChatAvailableReactions#14050ea6 peer:InputPeer available_reactions:Vector<string> = Updates;
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
messages.setDefaultReaction#d960c4d4 reaction:string = Bool;
messages.translateText#24ce6dee flags:# peer:flags.0?InputPeer msg_id:flags.0?int text:flags.1?string from_lang:flags.2?string to_lang:string = messages.TranslatedText;
messages.getUnreadReactions#e85bae1a peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readReactions#82e251d7 peer:InputPeer = messages.AffectedHistory;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@@ -1750,4 +1756,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
// LAYER 136
// LAYER 138

View File

@@ -10,7 +10,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="3.3.2.0" />
Version="3.5.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 3,3,2,0
PRODUCTVERSION 3,3,2,0
FILEVERSION 3,5,0,0
PRODUCTVERSION 3,5,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", "3.3.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "FileVersion", "3.5.0.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2022"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "3.3.2.0"
VALUE "ProductVersion", "3.5.0.0"
END
END
BLOCK "VarFileInfo"

View File

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

View File

@@ -252,12 +252,13 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) {
if (_photo) {
if (const auto image = _photo->image(Data::PhotoSize::Small)) {
const auto size = st::confirmInvitePhotoSize;
p.drawPixmap(
(width() - st::confirmInvitePhotoSize) / 2,
(width() - size) / 2,
st::confirmInvitePhotoTop,
image->pixCircled(
st::confirmInvitePhotoSize,
st::confirmInvitePhotoSize));
image->pix(
{ size, size },
{ .options = Images::Option::RoundCircle }));
}
} else if (_photoEmpty) {
_photoEmpty->paint(

View File

@@ -27,7 +27,7 @@ EntitiesInText EntitiesFromMTP(
for (const auto &entity : entities) {
switch (entity.type()) {
case mtpc_messageEntityUrl: { auto &d = entity.c_messageEntityUrl(); result.push_back({ EntityType::Url, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back({ EntityType::CustomUrl, d.voffset().v, d.vlength().v, Clean(qs(d.vurl())) }); } break;
case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back({ EntityType::CustomUrl, d.voffset().v, d.vlength().v, qs(d.vurl()) }); } break;
case mtpc_messageEntityEmail: { auto &d = entity.c_messageEntityEmail(); result.push_back({ EntityType::Email, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityHashtag: { auto &d = entity.c_messageEntityHashtag(); result.push_back({ EntityType::Hashtag, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityCashtag: { auto &d = entity.c_messageEntityCashtag(); result.push_back({ EntityType::Cashtag, d.voffset().v, d.vlength().v }); } break;
@@ -71,7 +71,7 @@ EntitiesInText EntitiesFromMTP(
case mtpc_messageEntityUnderline: { auto &d = entity.c_messageEntityUnderline(); result.push_back({ EntityType::Underline, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityStrike: { auto &d = entity.c_messageEntityStrike(); result.push_back({ EntityType::StrikeOut, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back({ EntityType::Code, d.voffset().v, d.vlength().v }); } break;
case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, Clean(qs(d.vlanguage())) }); } break;
case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, qs(d.vlanguage()) }); } break;
case mtpc_messageEntityBankCard: break; // Skipping cards.
case mtpc_messageEntitySpoiler: { auto &d = entity.c_messageEntitySpoiler(); result.push_back({ EntityType::Spoiler, d.voffset().v, d.vlength().v }); } break;
// #TODO entities

View File

@@ -0,0 +1,138 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_unread_things.h"
#include "data/data_peer.h"
#include "data/data_channel.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_unread_things.h"
#include "apiwrap.h"
namespace Api {
namespace {
constexpr auto kPreloadIfLess = 5;
constexpr auto kFirstRequestLimit = 10;
constexpr auto kNextRequestLimit = 100;
} // namespace
UnreadThings::UnreadThings(not_null<ApiWrap*> api) : _api(api) {
}
bool UnreadThings::trackMentions(PeerData *peer) const {
return peer && (peer->isChat() || peer->isMegagroup());
}
bool UnreadThings::trackReactions(PeerData *peer) const {
return trackMentions(peer) || (peer && peer->isUser());
}
void UnreadThings::preloadEnough(History *history) {
if (!history) {
return;
}
if (trackMentions(history->peer)) {
preloadEnoughMentions(history);
}
if (trackReactions(history->peer)) {
preloadEnoughReactions(history);
}
}
void UnreadThings::mediaAndMentionsRead(
const base::flat_set<MsgId> &readIds,
ChannelData *channel) {
for (const auto &msgId : readIds) {
_api->requestMessageData(channel, msgId, [=] {
const auto item = channel
? _api->session().data().message(channel->id, msgId)
: _api->session().data().nonChannelMessage(msgId);
if (item && item->mentionsMe()) {
item->markMediaAndMentionRead();
}
});
}
}
void UnreadThings::preloadEnoughMentions(not_null<History*> history) {
const auto fullCount = history->unreadMentions().count();
const auto loadedCount = history->unreadMentions().loadedCount();
const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount);
if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) {
requestMentions(history, loadedCount);
}
}
void UnreadThings::preloadEnoughReactions(not_null<History*> history) {
const auto fullCount = history->unreadReactions().count();
const auto loadedCount = history->unreadReactions().loadedCount();
const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount);
if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) {
requestReactions(history, loadedCount);
}
}
void UnreadThings::requestMentions(not_null<History*> history, int loaded) {
if (_mentionsRequests.contains(history)) {
return;
}
const auto offsetId = std::max(
history->unreadMentions().maxLoaded(),
MsgId(1));
const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit;
const auto addOffset = loaded ? -(limit + 1) : -limit;
const auto maxId = 0;
const auto minId = 0;
const auto requestId = _api->request(MTPmessages_GetUnreadMentions(
history->peer->input,
MTP_int(offsetId),
MTP_int(addOffset),
MTP_int(limit),
MTP_int(maxId),
MTP_int(minId)
)).done([=](const MTPmessages_Messages &result) {
_mentionsRequests.remove(history);
history->unreadMentions().addSlice(result, loaded);
}).fail([=] {
_mentionsRequests.remove(history);
}).send();
_mentionsRequests.emplace(history, requestId);
}
void UnreadThings::requestReactions(not_null<History*> history, int loaded) {
if (_reactionsRequests.contains(history)) {
return;
}
const auto offsetId = loaded
? std::max(history->unreadReactions().maxLoaded(), MsgId(1))
: MsgId(1);
const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit;
const auto addOffset = loaded ? -(limit + 1) : -limit;
const auto maxId = 0;
const auto minId = 0;
const auto requestId = _api->request(MTPmessages_GetUnreadReactions(
history->peer->input,
MTP_int(offsetId),
MTP_int(addOffset),
MTP_int(limit),
MTP_int(maxId),
MTP_int(minId)
)).done([=](const MTPmessages_Messages &result) {
_reactionsRequests.remove(history);
history->unreadReactions().addSlice(result, loaded);
}).fail([=] {
_reactionsRequests.remove(history);
}).send();
_reactionsRequests.emplace(history, requestId);
}
} // namespace UnreadThings

View File

@@ -0,0 +1,44 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class History;
class ApiWrap;
class PeerData;
class ChannelData;
namespace Api {
class UnreadThings final {
public:
explicit UnreadThings(not_null<ApiWrap*> api);
[[nodiscard]] bool trackMentions(PeerData *peer) const;
[[nodiscard]] bool trackReactions(PeerData *peer) const;
void preloadEnough(History *history);
void mediaAndMentionsRead(
const base::flat_set<MsgId> &readIds,
ChannelData *channel = nullptr);
private:
void preloadEnoughMentions(not_null<History*> history);
void preloadEnoughReactions(not_null<History*> history);
void requestMentions(not_null<History*> history, int loaded);
void requestReactions(not_null<History*> history, int loaded);
const not_null<ApiWrap*> _api;
base::flat_map<not_null<History*>, mtpRequestId> _mentionsRequests;
base::flat_map<not_null<History*>, mtpRequestId> _reactionsRequests;
};
} // namespace Api

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_chat_participants.h"
#include "api/api_text_entities.h"
#include "api/api_user_privacy.h"
#include "api/api_unread_things.h"
#include "main/main_session.h"
#include "main/main_account.h"
#include "mtproto/mtp_instance.h"
@@ -30,10 +31,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_folder.h"
#include "data/data_scheduled_messages.h"
#include "data/data_send_action.h"
#include "data/data_message_reactions.h"
#include "chat_helpers/emoji_interactions.h"
#include "lang/lang_cloud_manager.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_unread_things.h"
#include "core/application.h"
#include "storage/storage_account.h"
#include "storage/storage_facade.h"
@@ -46,7 +49,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/confirm_box.h"
#include "apiwrap.h"
#include "ui/text/format_values.h" // Ui::FormatPhone
#include "app.h" // App::quitting
namespace Api {
namespace {
@@ -912,7 +914,7 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
_lastWasOnline = isOnline;
_lastSetOnline = ms;
if (!App::quitting()) {
if (!Core::Quitting()) {
_onlineRequest = api().request(MTPaccount_UpdateStatus(
MTP_bool(!isOnline)
)).send();
@@ -1179,25 +1181,26 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
case mtpc_updateReadMessagesContents: {
const auto &d = update.c_updateReadMessagesContents();
auto possiblyReadMentions = base::flat_set<MsgId>();
auto unknownReadIds = base::flat_set<MsgId>();
for (const auto &msgId : d.vmessages().v) {
if (const auto item = _session->data().nonChannelMessage(msgId.v)) {
if (item->isUnreadMedia() || item->isUnreadMention()) {
item->markMediaRead();
item->markMediaAndMentionRead();
_session->data().requestItemRepaint(item);
if (item->out()
&& item->history()->peer->isUser()
&& !requestingDifference()) {
item->history()->peer->asUser()->madeAction(base::unixtime::now());
item->history()->peer->asUser()->madeAction(
base::unixtime::now());
}
}
} else {
// Perhaps it was an unread mention!
possiblyReadMentions.insert(msgId.v);
unknownReadIds.insert(msgId.v);
}
}
session().api().checkForUnreadMentions(possiblyReadMentions);
session().api().unreadThings().mediaAndMentionsRead(unknownReadIds);
} break;
case mtpc_updateReadHistoryInbox: {
@@ -1566,19 +1569,21 @@ void Updates::feedUpdate(const MTPUpdate &update) {
}
return;
}
auto possiblyReadMentions = base::flat_set<MsgId>();
auto unknownReadIds = base::flat_set<MsgId>();
for (const auto &msgId : d.vmessages().v) {
if (auto item = session().data().message(channel->id, msgId.v)) {
if (item->isUnreadMedia() || item->isUnreadMention()) {
item->markMediaRead();
item->markMediaAndMentionRead();
session().data().requestItemRepaint(item);
}
} else {
// Perhaps it was an unread mention!
possiblyReadMentions.insert(msgId.v);
unknownReadIds.insert(msgId.v);
}
}
session().api().checkForUnreadMentions(possiblyReadMentions, channel);
session().api().unreadThings().mediaAndMentionsRead(
unknownReadIds,
channel);
} break;
// Edited messages.
@@ -1626,6 +1631,16 @@ void Updates::feedUpdate(const MTPUpdate &update) {
d.vmsg_id().v);
if (item) {
item->updateReactions(&d.vreactions());
} else {
const auto hasUnreadReaction = Data::Reactions::HasUnread(
d.vreactions());
if (hasUnreadReaction || history->unreadReactions().has()) {
// The unread reactions count could change.
history->owner().histories().requestDialogEntry(history);
}
if (hasUnreadReaction) {
history->unreadReactions().checkAdd(d.vmsg_id().v);
}
}
}
} break;

View File

@@ -31,34 +31,60 @@ namespace {
constexpr auto kContextReactionsLimit = 50;
struct Peers {
std::vector<PeerId> list;
bool unknown = false;
};
inline bool operator==(const Peers &a, const Peers &b) noexcept {
return (a.list == b.list) && (a.unknown == b.unknown);
}
struct PeerWithReaction {
PeerId peer = 0;
QString reaction;
};
bool operator==(const PeerWithReaction &a, const PeerWithReaction &b) {
inline bool operator==(
const PeerWithReaction &a,
const PeerWithReaction &b) noexcept {
return (a.peer == b.peer) && (a.reaction == b.reaction);
}
struct PeersWithReactions {
std::vector<PeerWithReaction> list;
std::vector<PeerId> read;
int fullReactionsCount = 0;
bool unknown = false;
};
inline bool operator==(
const PeersWithReactions &a,
const PeersWithReactions &b) noexcept {
return (a.fullReactionsCount == b.fullReactionsCount)
&& (a.list == b.list)
&& (a.read == b.read)
&& (a.unknown == b.unknown);
}
struct CachedRead {
explicit CachedRead(PeerId unknownFlag)
: list(std::vector<PeerId>{ unknownFlag }) {
CachedRead()
: data(Peers{ .unknown = true }) {
}
rpl::variable<std::vector<PeerId>> list;
rpl::variable<Peers> data;
mtpRequestId requestId = 0;
};
struct CachedReacted {
explicit CachedReacted(PeerId unknownFlag)
: list(
std::vector<PeerWithReaction>{ PeerWithReaction{ unknownFlag } }) {
CachedReacted()
: data(PeersWithReactions{ .unknown = true }) {
}
rpl::variable<std::vector<PeerWithReaction>> list;
rpl::variable<PeersWithReactions> data;
mtpRequestId requestId = 0;
};
struct Context {
base::flat_map<not_null<HistoryItem*>, CachedRead> cachedRead;
base::flat_map<not_null<HistoryItem*>, CachedReacted> cachedReacted;
base::flat_map<
not_null<HistoryItem*>,
base::flat_map<QString, CachedReacted>> cachedReacted;
base::flat_map<not_null<Main::Session*>, rpl::lifetime> subscriptions;
[[nodiscard]] CachedRead &cacheRead(not_null<HistoryItem*> item) {
@@ -66,21 +92,18 @@ struct Context {
if (i != end(cachedRead)) {
return i->second;
}
return cachedRead.emplace(
item,
CachedRead(item->history()->session().userPeerId())
).first->second;
return cachedRead.emplace(item, CachedRead()).first->second;
}
[[nodiscard]] CachedReacted &cacheReacted(not_null<HistoryItem*> item) {
const auto i = cachedReacted.find(item);
if (i != end(cachedReacted)) {
[[nodiscard]] CachedReacted &cacheReacted(
not_null<HistoryItem*> item,
const QString &reaction) {
auto &map = cachedReacted[item];
const auto i = map.find(reaction);
if (i != end(map)) {
return i->second;
}
return cachedReacted.emplace(
item,
CachedReacted(item->history()->session().userPeerId())
).first->second;
return map.emplace(reaction, CachedReacted()).first->second;
}
};
@@ -124,9 +147,11 @@ struct State {
item->history()->session().api().request(requestId).cancel();
}
}
for (auto &[item, entry] : i->second->cachedReacted) {
if (const auto requestId = entry.requestId) {
item->history()->session().api().request(requestId).cancel();
for (auto &[item, map] : i->second->cachedReacted) {
for (auto &[reaction, entry] : map) {
if (const auto requestId = entry.requestId) {
item->history()->session().api().request(requestId).cancel();
}
}
}
contexts.erase(i);
@@ -134,7 +159,9 @@ struct State {
return result;
}
[[nodiscard]] not_null<Context*> PreparedContextAt(not_null<QWidget*> key, not_null<Main::Session*> session) {
[[nodiscard]] not_null<Context*> PreparedContextAt(
not_null<QWidget*> key,
not_null<Main::Session*> session) {
const auto context = ContextAt(key);
if (context->subscriptions.contains(session)) {
return context;
@@ -149,7 +176,9 @@ struct State {
}
const auto j = context->cachedReacted.find(update.item);
if (j != end(context->cachedReacted)) {
session->api().request(j->second.requestId).cancel();
for (auto &[reaction, entry] : j->second) {
session->api().request(entry.requestId).cancel();
}
context->cachedReacted.erase(j);
}
}, context->subscriptions[session]);
@@ -163,21 +192,6 @@ struct State {
return result;
}
[[nodiscard]] bool ListUnknown(
const std::vector<PeerId> &list,
not_null<HistoryItem*> item) {
return (list.size() == 1)
&& (list.front() == item->history()->session().userPeerId());
}
[[nodiscard]] bool ListUnknown(
const std::vector<PeerWithReaction> &list,
not_null<HistoryItem*> item) {
return (list.size() == 1)
&& list.front().reaction.isEmpty()
&& (list.front().peer == item->history()->session().userPeerId());
}
[[nodiscard]] Ui::WhoReadType DetectSeenType(not_null<HistoryItem*> item) {
if (const auto media = item->media()) {
if (!media->webpage()) {
@@ -193,7 +207,7 @@ struct State {
return Ui::WhoReadType::Seen;
}
[[nodiscard]] rpl::producer<std::vector<PeerId>> WhoReadIds(
[[nodiscard]] rpl::producer<Peers> WhoReadIds(
not_null<HistoryItem*> item,
not_null<QWidget*> context) {
auto weak = QPointer<QWidget>(context.get());
@@ -213,33 +227,39 @@ struct State {
).done([=](const MTPVector<MTPlong> &result) {
auto &entry = context->cacheRead(item);
entry.requestId = 0;
auto peers = std::vector<PeerId>();
peers.reserve(std::max(int(result.v.size()), 1));
auto parsed = Peers();
parsed.list.reserve(result.v.size());
for (const auto &id : result.v) {
peers.push_back(UserId(id));
parsed.list.push_back(UserId(id));
}
entry.list = std::move(peers);
entry.data = std::move(parsed);
}).fail([=] {
auto &entry = context->cacheRead(item);
entry.requestId = 0;
if (ListUnknown(entry.list.current(), item)) {
entry.list = std::vector<PeerId>();
if (entry.data.current().unknown) {
entry.data = Peers();
}
}).send();
}
return entry.list.value().start_existing(consumer);
return entry.data.value().start_existing(consumer);
};
}
[[nodiscard]] std::vector < PeerWithReaction> WithEmptyReactions(
const std::vector<PeerId> &peers) {
return peers | ranges::views::transform([](PeerId peer) {
return PeerWithReaction{ .peer = peer };
}) | ranges::to_vector;
[[nodiscard]] PeersWithReactions WithEmptyReactions(
Peers &&peers) {
auto result = PeersWithReactions{
.list = peers.list | ranges::views::transform([](PeerId peer) {
return PeerWithReaction{.peer = peer };
}) | ranges::to_vector,
.unknown = peers.unknown,
};
result.read = std::move(peers.list);
return result;
}
[[nodiscard]] rpl::producer<std::vector<PeerWithReaction>> WhoReactedIds(
[[nodiscard]] rpl::producer<PeersWithReactions> WhoReactedIds(
not_null<HistoryItem*> item,
const QString &reaction,
not_null<QWidget*> context) {
auto weak = QPointer<QWidget>(context.get());
const auto session = &item->history()->session();
@@ -248,68 +268,74 @@ struct State {
return rpl::lifetime();
}
const auto context = PreparedContextAt(weak.data(), session);
auto &entry = context->cacheReacted(item);
auto &entry = context->cacheReacted(item, reaction);
if (!entry.requestId) {
using Flag = MTPmessages_GetMessageReactionsList::Flag;
entry.requestId = session->api().request(
MTPmessages_GetMessageReactionsList(
MTP_flags(0),
MTP_flags(reaction.isEmpty()
? Flag(0)
: Flag::f_reaction),
item->history()->peer->input,
MTP_int(item->id),
MTPstring(), // reaction
MTP_string(reaction),
MTPstring(), // offset
MTP_int(kContextReactionsLimit)
)
).done([=](const MTPmessages_MessageReactionsList &result) {
auto &entry = context->cacheReacted(item);
auto &entry = context->cacheReacted(item, reaction);
entry.requestId = 0;
result.match([&](
const MTPDmessages_messageReactionsList &data) {
session->data().processUsers(data.vusers());
session->data().processChats(data.vchats());
auto peers = std::vector<PeerWithReaction>();
peers.reserve(data.vreactions().v.size());
auto parsed = PeersWithReactions{
.fullReactionsCount = data.vcount().v,
};
parsed.list.reserve(data.vreactions().v.size());
for (const auto &vote : data.vreactions().v) {
vote.match([&](const auto &data) {
peers.push_back(PeerWithReaction{
.peer = peerFromUser(data.vuser_id()),
parsed.list.push_back(PeerWithReaction{
.peer = peerFromMTP(data.vpeer_id()),
.reaction = qs(data.vreaction()),
});
});
}
entry.list = std::move(peers);
entry.data = std::move(parsed);
});
}).fail([=] {
auto &entry = context->cacheReacted(item);
auto &entry = context->cacheReacted(item, reaction);
entry.requestId = 0;
if (ListUnknown(entry.list.current(), item)) {
entry.list = std::vector<PeerWithReaction>();
if (entry.data.current().unknown) {
entry.data = PeersWithReactions();
}
}).send();
}
return entry.list.value().start_existing(consumer);
return entry.data.value().start_existing(consumer);
};
}
[[nodiscard]] auto WhoReadOrReactedIds(
not_null<HistoryItem*> item,
not_null<QWidget*> context)
-> rpl::producer<std::vector<PeerWithReaction>> {
-> rpl::producer<PeersWithReactions> {
return rpl::combine(
WhoReactedIds(item, context),
WhoReactedIds(item, QString(), context),
WhoReadIds(item, context)
) | rpl::map([=](
std::vector<PeerWithReaction> reacted,
std::vector<PeerId> read) {
if (ListUnknown(reacted, item) || ListUnknown(read, item)) {
return reacted;
) | rpl::map([=](PeersWithReactions &&reacted, Peers &&read) {
if (reacted.unknown || read.unknown) {
return PeersWithReactions{ .unknown = true };
}
for (const auto &peer : read) {
if (!ranges::contains(reacted, peer, &PeerWithReaction::peer)) {
reacted.push_back({ .peer = peer });
auto &list = reacted.list;
for (const auto &peer : read.list) {
if (!ranges::contains(list, peer, &PeerWithReaction::peer)) {
list.push_back({ .peer = peer });
}
}
return reacted;
reacted.read = std::move(read.list);
return std::move(reacted);
});
}
@@ -418,6 +444,104 @@ void RegenerateParticipants(not_null<State*> state, int small, int large) {
RegenerateUserpics(state, small, large);
}
rpl::producer<Ui::WhoReadContent> WhoReacted(
not_null<HistoryItem*> item,
const QString &reaction,
not_null<QWidget*> context,
const style::WhoRead &st,
std::shared_ptr<WhoReadList> whoReadIds) {
const auto small = st.userpics.size;
const auto large = st.photoSize;
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
const auto resolveWhoRead = reaction.isEmpty()
&& WhoReadExists(item);
const auto state = lifetime.make_state<State>();
const auto pushNext = [=] {
consumer.put_next_copy(state->current);
};
const auto resolveWhoReacted = !reaction.isEmpty()
|| item->canViewReactions();
auto idsWithReactions = (resolveWhoRead && resolveWhoReacted)
? WhoReadOrReactedIds(item, context)
: resolveWhoRead
? (WhoReadIds(item, context) | rpl::map(WithEmptyReactions))
: WhoReactedIds(item, reaction, context);
state->current.type = resolveWhoRead
? DetectSeenType(item)
: Ui::WhoReadType::Reacted;
if (resolveWhoReacted) {
const auto &list = item->reactions();
state->current.fullReactionsCount = reaction.isEmpty()
? ranges::accumulate(
list,
0,
ranges::plus{},
[](const auto &pair) { return pair.second; })
: list.contains(reaction)
? list.find(reaction)->second
: 0;
// #TODO reactions
state->current.singleReaction = !reaction.isEmpty()
? reaction
: (list.size() == 1)
? list.front().first
: QString();
}
std::move(
idsWithReactions
) | rpl::start_with_next([=](PeersWithReactions &&peers) {
if (peers.unknown) {
state->userpics.clear();
consumer.put_next(Ui::WhoReadContent{
.type = state->current.type,
.fullReactionsCount = state->current.fullReactionsCount,
.fullReadCount = state->current.fullReadCount,
.unknown = true,
});
return;
}
state->current.fullReadCount = int(peers.read.size());
state->current.fullReactionsCount = peers.fullReactionsCount;
if (whoReadIds) {
whoReadIds->list = (peers.read.size() > peers.list.size())
? std::move(peers.read)
: std::vector<PeerId>();
}
if (UpdateUserpics(state, item, peers.list)) {
RegenerateParticipants(state, small, large);
pushNext();
} else if (peers.list.empty()) {
pushNext();
}
}, lifetime);
item->history()->session().downloaderTaskFinished(
) | rpl::filter([=] {
return state->someUserpicsNotLoaded && !state->scheduled;
}) | rpl::start_with_next([=] {
for (const auto &userpic : state->userpics) {
if (userpic.peer->userpicUniqueKey(userpic.view)
!= userpic.uniqueKey) {
state->scheduled = true;
crl::on_main(&state->guard, [=] {
state->scheduled = false;
RegenerateUserpics(state, small, large);
pushNext();
});
return;
}
}
}, lifetime);
return lifetime;
};
}
} // namespace
bool WhoReadExists(not_null<HistoryItem*> item) {
@@ -462,78 +586,17 @@ bool WhoReactedExists(not_null<HistoryItem*> item) {
rpl::producer<Ui::WhoReadContent> WhoReacted(
not_null<HistoryItem*> item,
not_null<QWidget*> context,
const style::WhoRead &st,
std::shared_ptr<WhoReadList> whoReadIds) {
return WhoReacted(item, QString(), context, st, std::move(whoReadIds));
}
rpl::producer<Ui::WhoReadContent> WhoReacted(
not_null<HistoryItem*> item,
const QString &reaction,
not_null<QWidget*> context,
const style::WhoRead &st) {
const auto small = st.userpics.size;
const auto large = st.photoSize;
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
const auto resolveWhoRead = WhoReadExists(item);
const auto state = lifetime.make_state<State>();
const auto pushNext = [=] {
consumer.put_next_copy(state->current);
};
const auto resolveWhoReacted = item->canViewReactions();
auto idsWithReactions = (resolveWhoRead && resolveWhoReacted)
? WhoReadOrReactedIds(item, context)
: resolveWhoRead
? (WhoReadIds(item, context) | rpl::map(WithEmptyReactions))
: WhoReactedIds(item, context);
state->current.type = resolveWhoRead
? DetectSeenType(item)
: Ui::WhoReadType::Reacted;
if (resolveWhoReacted) {
const auto &list = item->reactions();
state->current.fullReactionsCount = ranges::accumulate(
list,
0,
ranges::plus{},
[](const auto &pair) { return pair.second; });
// #TODO reactions
state->current.mostPopularReaction = item->reactions().front().first;
}
std::move(
idsWithReactions
) | rpl::start_with_next([=](
const std::vector<PeerWithReaction> &peers) {
if (ListUnknown(peers, item)) {
state->userpics.clear();
consumer.put_next(Ui::WhoReadContent{
.type = state->current.type,
.unknown = true,
});
return;
} else if (UpdateUserpics(state, item, peers)) {
RegenerateParticipants(state, small, large);
pushNext();
} else if (peers.empty()) {
pushNext();
}
}, lifetime);
item->history()->session().downloaderTaskFinished(
) | rpl::filter([=] {
return state->someUserpicsNotLoaded && !state->scheduled;
}) | rpl::start_with_next([=] {
for (const auto &userpic : state->userpics) {
if (userpic.peer->userpicUniqueKey(userpic.view)
!= userpic.uniqueKey) {
state->scheduled = true;
crl::on_main(&state->guard, [=] {
state->scheduled = false;
RegenerateUserpics(state, small, large);
pushNext();
});
return;
}
}
}, lifetime);
return lifetime;
};
return WhoReacted(item, reaction, context, st, nullptr);
}
} // namespace Api

View File

@@ -15,6 +15,7 @@ struct WhoRead;
namespace Ui {
struct WhoReadContent;
enum class WhoReadType;
} // namespace Ui
namespace Api {
@@ -22,10 +23,21 @@ namespace Api {
[[nodiscard]] bool WhoReadExists(not_null<HistoryItem*> item);
[[nodiscard]] bool WhoReactedExists(not_null<HistoryItem*> item);
struct WhoReadList {
std::vector<PeerId> list;
Ui::WhoReadType type = {};
};
// The context must be destroyed before the session holding this item.
[[nodiscard]] rpl::producer<Ui::WhoReadContent> WhoReacted(
not_null<HistoryItem*> item,
not_null<QWidget*> context,
const style::WhoRead &st); // Cache results for this lifetime.
not_null<QWidget*> context, // Cache results for this lifetime.
const style::WhoRead &st,
std::shared_ptr<WhoReadList> whoReadIds = nullptr);
[[nodiscard]] rpl::producer<Ui::WhoReadContent> WhoReacted(
not_null<HistoryItem*> item,
const QString &reaction,
not_null<QWidget*> context, // Cache results for this lifetime.
const style::WhoRead &st);
} // namespace Api

View File

@@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_user_privacy.h"
#include "api/api_views.h"
#include "api/api_confirm_phone.h"
#include "api/api_unread_things.h"
#include "data/stickers/data_stickers.h"
#include "data/data_drafts.h"
#include "data/data_changes.h"
@@ -51,7 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "base/unixtime.h"
#include "base/random.h"
#include "base/qt_adapters.h"
#include "base/qt/qt_common_adapters.h"
#include "base/call_delayed.h"
#include "lang/lang_keys.h"
#include "mainwindow.h"
@@ -73,6 +74,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "inline_bots/inline_bot_result.h"
#include "chat_helpers/message_field.h"
#include "ui/item_text_options.h"
#include "ui/text/text_utilities.h"
#include "ui/emoji_config.h"
#include "ui/chat/attach/attach_prepare.h"
#include "ui/toasts/common_toasts.h"
@@ -86,7 +88,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_media_prepare.h"
#include "storage/storage_account.h"
#include "facades.h"
#include "app.h" // App::quitting
namespace {
@@ -96,9 +97,6 @@ constexpr auto kSaveCloudDraftTimeout = 1000;
constexpr auto kTopPromotionInterval = TimeId(60 * 60);
constexpr auto kTopPromotionMinDelay = TimeId(10);
constexpr auto kSmallDelayMs = 5;
constexpr auto kUnreadMentionsPreloadIfLess = 5;
constexpr auto kUnreadMentionsFirstRequestLimit = 10;
constexpr auto kUnreadMentionsNextRequestLimit = 100;
constexpr auto kSharedMediaLimit = 100;
constexpr auto kReadFeaturedSetsTimeout = crl::time(1000);
constexpr auto kFileLoaderQueueStopTimeout = crl::time(5000);
@@ -142,7 +140,8 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
, _confirmPhone(std::make_unique<Api::ConfirmPhone>(this))
, _peerPhoto(std::make_unique<Api::PeerPhoto>(this))
, _polls(std::make_unique<Api::Polls>(this))
, _chatParticipants(std::make_unique<Api::ChatParticipants>(this)) {
, _chatParticipants(std::make_unique<Api::ChatParticipants>(this))
, _unreadThings(std::make_unique<Api::UnreadThings>(this)) {
crl::on_main(session, [=] {
// You can't use _session->lifetime() in the constructor,
// only queued, because it is not constructed yet.
@@ -470,13 +469,14 @@ void ApiWrap::sendMessageFail(
Ui::show(Box<Ui::InformBox>(
PeerFloodErrorText(&session(), PeerFloodType::Send)));
} else if (error.type() == qstr("USER_BANNED_IN_CHANNEL")) {
const auto link = textcmdLink(
session().createInternalLinkFull(qsl("spambot")),
tr::lng_cant_more_info(tr::now));
const auto link = Ui::Text::Link(
tr::lng_cant_more_info(tr::now),
session().createInternalLinkFull(qsl("spambot")));
Ui::show(Box<Ui::InformBox>(tr::lng_error_public_groups_denied(
tr::now,
lt_more_info,
link)));
link,
Ui::Text::WithEntities)));
} else if (error.type().startsWith(qstr("SLOWMODE_WAIT_"))) {
const auto chop = qstr("SLOWMODE_WAIT_").size();
const auto left = base::StringViewMid(error.type(), chop).toInt();
@@ -1286,7 +1286,7 @@ void ApiWrap::migrateFail(not_null<PeerData*> peer, const QString &error) {
}
}
void ApiWrap::markMediaRead(
void ApiWrap::markContentsRead(
const base::flat_set<not_null<HistoryItem*>> &items) {
auto markedIds = QVector<MTPint>();
auto channelMarkedIds = base::flat_map<
@@ -1294,12 +1294,7 @@ void ApiWrap::markMediaRead(
QVector<MTPint>>();
markedIds.reserve(items.size());
for (const auto &item : items) {
if ((!item->isUnreadMedia() || item->out())
&& !item->isUnreadMention()) {
continue;
}
item->markMediaRead();
if (!item->isRegular()) {
if (!item->markContentsRead(true) || !item->isRegular()) {
continue;
}
if (const auto channel = item->history()->peer->asChannel()) {
@@ -1323,13 +1318,8 @@ void ApiWrap::markMediaRead(
}
}
void ApiWrap::markMediaRead(not_null<HistoryItem*> item) {
if ((!item->isUnreadMedia() || item->out())
&& !item->isUnreadMention()) {
return;
}
item->markMediaRead();
if (!item->isRegular()) {
void ApiWrap::markContentsRead(not_null<HistoryItem*> item) {
if (!item->markContentsRead(true) || !item->isRegular()) {
return;
}
const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id));
@@ -2125,7 +2115,7 @@ bool ApiWrap::isQuitPrevent() {
void ApiWrap::checkQuitPreventFinished() {
if (_draftsSaveRequestIds.empty()) {
if (App::quitting()) {
if (Core::Quitting()) {
LOG(("ApiWrap doesn't prevent quit any more."));
}
Core::App().quitPreventFinished();
@@ -2909,45 +2899,6 @@ void ApiWrap::jumpToHistoryDate(not_null<PeerData*> peer, const QDate &date) {
}
}
void ApiWrap::preloadEnoughUnreadMentions(not_null<History*> history) {
auto fullCount = history->getUnreadMentionsCount();
auto loadedCount = history->getUnreadMentionsLoadedCount();
auto allLoaded = (fullCount >= 0) ? (loadedCount >= fullCount) : false;
if (fullCount < 0 || loadedCount >= kUnreadMentionsPreloadIfLess || allLoaded) {
return;
}
if (_unreadMentionsRequests.contains(history)) {
return;
}
auto offsetId = loadedCount ? history->getMaxLoadedUnreadMention() : 1;
auto limit = loadedCount ? kUnreadMentionsNextRequestLimit : kUnreadMentionsFirstRequestLimit;
auto addOffset = loadedCount ? -(limit + 1) : -limit;
auto maxId = 0;
auto minId = 0;
auto requestId = request(MTPmessages_GetUnreadMentions(history->peer->input, MTP_int(offsetId), MTP_int(addOffset), MTP_int(limit), MTP_int(maxId), MTP_int(minId))).done([this, history](const MTPmessages_Messages &result) {
_unreadMentionsRequests.remove(history);
history->addUnreadMentionsSlice(result);
}).fail([this, history] {
_unreadMentionsRequests.remove(history);
}).send();
_unreadMentionsRequests.emplace(history, requestId);
}
void ApiWrap::checkForUnreadMentions(
const base::flat_set<MsgId> &possiblyReadMentions,
ChannelData *channel) {
for (const auto &msgId : possiblyReadMentions) {
requestMessageData(channel, msgId, [=] {
const auto item = channel
? _session->data().message(channel->id, msgId)
: _session->data().nonChannelMessage(msgId);
if (item && item->mentionsMe()) {
item->markMediaRead();
}
});
}
}
void ApiWrap::requestSharedMediaCount(
not_null<PeerData*> peer,
Storage::SharedMediaType type) {
@@ -4145,3 +4096,7 @@ Api::Polls &ApiWrap::polls() {
Api::ChatParticipants &ApiWrap::chatParticipants() {
return *_chatParticipants;
}
Api::UnreadThings &ApiWrap::unreadThings() {
return *_unreadThings;
}

View File

@@ -67,6 +67,7 @@ class ConfirmPhone;
class PeerPhoto;
class Polls;
class ChatParticipants;
class UnreadThings;
namespace details {
@@ -206,8 +207,9 @@ public:
FnMut<void(not_null<ChannelData*>)> done,
Fn<void(const QString &)> fail = nullptr);
void markMediaRead(const base::flat_set<not_null<HistoryItem*>> &items);
void markMediaRead(not_null<HistoryItem*> item);
void markContentsRead(
const base::flat_set<not_null<HistoryItem*>> &items);
void markContentsRead(not_null<HistoryItem*> item);
void deleteAllFromParticipant(
not_null<ChannelData*> channel,
@@ -250,11 +252,6 @@ public:
void jumpToDate(Dialogs::Key chat, const QDate &date);
void preloadEnoughUnreadMentions(not_null<History*> history);
void checkForUnreadMentions(
const base::flat_set<MsgId> &possiblyReadMentions,
ChannelData *channel = nullptr);
using SliceType = Data::LoadDirection;
void requestSharedMedia(
not_null<PeerData*> peer,
@@ -356,6 +353,7 @@ public:
[[nodiscard]] Api::PeerPhoto &peerPhoto();
[[nodiscard]] Api::Polls &polls();
[[nodiscard]] Api::ChatParticipants &chatParticipants();
[[nodiscard]] Api::UnreadThings &unreadThings();
void updatePrivacyLastSeens();
@@ -562,8 +560,6 @@ private:
mtpRequestId _contactsRequestId = 0;
mtpRequestId _contactsStatusesRequestId = 0;
base::flat_map<not_null<History*>, mtpRequestId> _unreadMentionsRequests;
base::flat_set<std::tuple<
not_null<PeerData*>,
SharedMediaType,
@@ -636,6 +632,7 @@ private:
const std::unique_ptr<Api::PeerPhoto> _peerPhoto;
const std::unique_ptr<Api::Polls> _polls;
const std::unique_ptr<Api::ChatParticipants> _chatParticipants;
const std::unique_ptr<Api::UnreadThings> _unreadThings;
mtpRequestId _wallPaperRequestId = 0;
QString _wallPaperSlug;

View File

@@ -1,123 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "app.h"
#include "history/view/history_view_element.h"
#include "core/update_checker.h"
#include "core/sandbox.h"
#include "core/application.h"
#include "mainwindow.h"
#include <QtCore/QBuffer>
#include <QtGui/QFontDatabase>
namespace {
App::LaunchState _launchState = App::Launched;
HistoryView::Element *hoveredItem = nullptr,
*pressedItem = nullptr,
*hoveredLinkItem = nullptr,
*pressedLinkItem = nullptr,
*mousedItem = nullptr;
} // namespace
namespace App {
void hoveredItem(HistoryView::Element *item) {
::hoveredItem = item;
}
HistoryView::Element *hoveredItem() {
return ::hoveredItem;
}
void pressedItem(HistoryView::Element *item) {
::pressedItem = item;
}
HistoryView::Element *pressedItem() {
return ::pressedItem;
}
void hoveredLinkItem(HistoryView::Element *item) {
::hoveredLinkItem = item;
}
HistoryView::Element *hoveredLinkItem() {
return ::hoveredLinkItem;
}
void pressedLinkItem(HistoryView::Element *item) {
::pressedLinkItem = item;
}
HistoryView::Element *pressedLinkItem() {
return ::pressedLinkItem;
}
void mousedItem(HistoryView::Element *item) {
::mousedItem = item;
}
HistoryView::Element *mousedItem() {
return ::mousedItem;
}
void clearMousedItems() {
hoveredItem(nullptr);
pressedItem(nullptr);
hoveredLinkItem(nullptr);
pressedLinkItem(nullptr);
mousedItem(nullptr);
}
void quit() {
if (quitting()) {
return;
} else if (Core::IsAppLaunched()
&& Core::App().exportPreventsQuit()) {
return;
}
setLaunchState(QuitRequested);
if (auto window = App::wnd()) {
if (!Core::Sandbox::Instance().isSavingSession()) {
window->hide();
}
}
Core::Application::QuitAttempt();
}
bool quitting() {
return _launchState != Launched;
}
LaunchState launchState() {
return _launchState;
}
void setLaunchState(LaunchState state) {
_launchState = state;
}
void restart() {
using namespace Core;
const auto updateReady = !UpdaterDisabled()
&& (UpdateChecker().state() == UpdateChecker::State::Ready);
if (updateReady) {
cSetRestartingUpdate(true);
} else {
cSetRestarting(true);
cSetRestartingToSettings(true);
}
App::quit();
}
}

View File

@@ -1,38 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace HistoryView {
class Element;
} // namespace HistoryView
namespace App {
void hoveredItem(HistoryView::Element *item);
HistoryView::Element *hoveredItem();
void pressedItem(HistoryView::Element *item);
HistoryView::Element *pressedItem();
void hoveredLinkItem(HistoryView::Element *item);
HistoryView::Element *hoveredLinkItem();
void pressedLinkItem(HistoryView::Element *item);
HistoryView::Element *pressedLinkItem();
void mousedItem(HistoryView::Element *item);
HistoryView::Element *mousedItem();
void clearMousedItems();
enum LaunchState {
Launched = 0,
QuitRequested = 1,
QuitProcessed = 2,
};
void quit();
bool quitting();
LaunchState launchState();
void setLaunchState(LaunchState state);
void restart();
};

View File

@@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/special_buttons.h"
#include "ui/special_fields.h"
#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
#include "ui/unread_badge.h"
#include "ui/ui_utility.h"
#include "data/data_channel.h"
@@ -112,16 +113,19 @@ style::InputField CreateBioFieldStyle() {
return result;
}
QString PeerFloodErrorText(
TextWithEntities PeerFloodErrorText(
not_null<Main::Session*> session,
PeerFloodType type) {
const auto link = textcmdLink(
session->createInternalLinkFull(qsl("spambot")),
tr::lng_cant_more_info(tr::now));
if (type == PeerFloodType::InviteGroup) {
return tr::lng_cant_invite_not_contact(tr::now, lt_more_info, link);
}
return tr::lng_cant_send_to_not_contact(tr::now, lt_more_info, link);
const auto link = Ui::Text::Link(
tr::lng_cant_more_info(tr::now),
session->createInternalLinkFull(qsl("spambot")));
return ((type == PeerFloodType::InviteGroup)
? tr::lng_cant_invite_not_contact
: tr::lng_cant_send_to_not_contact)(
tr::now,
lt_more_info,
link,
Ui::Text::WithEntities);
}
void ShowAddParticipantsError(
@@ -167,6 +171,14 @@ void ShowAddParticipantsError(
}
}
const auto hasBot = ranges::any_of(users, &UserData::isBot);
if (error == u"PEER_FLOOD"_q) {
const auto type = (chat->isChat() || chat->isMegagroup())
? PeerFloodType::InviteGroup
: PeerFloodType::InviteChannel;
const auto text = PeerFloodErrorText(&chat->session(), type);
Ui::show(Box<Ui::InformBox>(text), Ui::LayerOption::KeepOther);
return;
}
const auto text = [&] {
if (error == u"USER_BOT"_q) {
return tr::lng_cant_invite_bot_to_channel(tr::now);
@@ -184,11 +196,6 @@ void ShowAddParticipantsError(
return tr::lng_bot_already_in_group(tr::now);
} else if (error == u"BOT_GROUPS_BLOCKED"_q) {
return tr::lng_error_cant_add_bot(tr::now);
} else if (error == u"PEER_FLOOD"_q) {
const auto type = (chat->isChat() || chat->isMegagroup())
? PeerFloodType::InviteGroup
: PeerFloodType::InviteChannel;
return PeerFloodErrorText(&chat->session(), type);
} else if (error == u"ADMINS_TOO_MUCH"_q) {
return ((chat->isChat() || chat->isMegagroup())
? tr::lng_error_admin_limit
@@ -1521,11 +1528,10 @@ RevokePublicLinkBox::Inner::Inner(
st::contactsNameStyle,
peer->name,
Ui::NameTextOptions());
row.status.setText(
row.status.setMarkedText(
st::defaultTextStyle,
_session->createInternalLink(
textcmdLink(1, peer->userName())),
Ui::DialogTextOptions());
Ui::Text::Link(peer->userName())));
_rows.push_back(std::move(row));
}
}

View File

@@ -46,7 +46,7 @@ enum class PeerFloodType {
[[nodiscard]] style::InputField CreateBioFieldStyle();
[[nodiscard]] QString PeerFloodErrorText(
[[nodiscard]] TextWithEntities PeerFloodErrorText(
not_null<Main::Session*> session,
PeerFloodType type);
void ShowAddParticipantsError(

View File

@@ -302,7 +302,7 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
base::unixtime::now(),
out ? history->session().userId() : peerToUser(history->peer->id),
QString(),
TextWithEntities{ TextUtilities::Clean(text) },
TextWithEntities{ text },
MTP_messageMediaEmpty(),
HistoryMessageMarkupData(),
groupedId);
@@ -321,15 +321,11 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
const auto takeHeight = (width > height)
? size
: (height * size / width);
return Images::prepare(
image,
takeWidth * cIntRetinaFactor(),
takeHeight * cIntRetinaFactor(),
Images::Option::Smooth
| Images::Option::TransparentBackground
| blur,
size,
size);
const auto ratio = style::DevicePixelRatio();
return Images::Prepare(image, QSize(takeWidth, takeHeight) * ratio, {
.options = Images::Option::TransparentBackground | blur,
.outer = { size, size },
});
}
[[nodiscard]] QImage PrepareScaledFromFull(
@@ -667,7 +663,7 @@ void BackgroundPreviewBox::setScaledFromThumb() {
_paper.backgroundColors(),
_paper.gradientRotation(),
_paper.patternOpacity(),
_paper.document() ? Images::Option::Blurred : Images::Option(0));
_paper.document() ? Images::Option::Blur : Images::Option());
auto blurred = (_paper.document() || _paper.isPattern())
? QImage()
: PrepareScaledNonPattern(

View File

@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h"
#include "ui/effects/radial_animation.h"
#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
#include "ui/basic_click_handlers.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -312,9 +313,12 @@ void ProxyRow::updateFields(View &&view) {
}
_view = std::move(view);
const auto endpoint = _view.host + ':' + QString::number(_view.port);
_title.setText(
_title.setMarkedText(
st::proxyRowTitleStyle,
_view.type + ' ' + textcmdLink(1, endpoint),
TextWithEntities()
.append(_view.type)
.append(' ')
.append(Ui::Text::Link(endpoint, {})),
Ui::ItemTextDefaultOptions());
const auto state = _view.state;
@@ -522,7 +526,7 @@ void ProxyRow::showMenu() {
const QString &text,
Fn<void()> callback,
const style::icon *icon) {
return _menu->addAction(text, std::move(callback));
return _menu->addAction(text, std::move(callback), icon);
};
addAction(tr::lng_proxy_menu_edit(tr::now), [=] {
_editClicks.fire({});

View File

@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/toast/toast.h"
#include "ui/text/text_utilities.h"
#include "main/main_session.h"
#include "core/application.h"
#include "core/core_settings.h"
@@ -190,9 +191,12 @@ not_null<Ui::FlatLabel*> CreateWarningLabel(
const auto value = valueLimit - length;
const auto shown = (value < warnLimit)
&& (field->height() > st::createPollOptionField.heightMin);
result->setRichText((value >= 0)
? QString::number(value)
: textcmdLink(1, QString::number(value)));
if (value >= 0) {
result->setText(QString::number(value));
} else {
result->setMarkedText(Ui::Text::PlainLink(
QString::number(value)));
}
result->setVisible(shown);
}));
});

View File

@@ -28,8 +28,7 @@ namespace {
TextParseOptions kInformBoxTextOptions = {
(TextParseLinks
| TextParseMultiline
| TextParseMarkdown
| TextParseRichText), // flags
| TextParseMarkdown), // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir

View File

@@ -32,7 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_layers.h"
#include "styles/style_passport.h"
#include "styles/style_boxes.h"
#include "base/qt_adapters.h"
#include "base/qt/qt_common_adapters.h"
namespace {

View File

@@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_changes.h"
#include "data/data_message_reactions.h"
#include "data/data_peer_values.h"
#include "history/admin_log/history_admin_log_section.h"
#include "info/profile/info_profile_values.h"
#include "lang/lang_keys.h"
@@ -1060,7 +1061,7 @@ void Controller::fillManageSection() {
!_peer->isBroadcast(),
session->data().reactions().list(
Data::Reactions::Type::Active),
session->data().reactions().list(_peer),
*Data::PeerAllowedReactions(_peer),
done));
},
st::infoIconReactions);

View File

@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "main/main_session.h"
#include "apiwrap.h"
#include "lottie/lottie_icon.h"
#include "lang/lang_keys.h"
#include "ui/widgets/buttons.h"
#include "info/profile/info_profile_icon.h"
@@ -32,6 +33,7 @@ void AddReactionIcon(
not_null<DocumentData*> document) {
struct State {
std::shared_ptr<Data::DocumentMedia> media;
std::unique_ptr<Lottie::Icon> icon;
QImage image;
};
@@ -45,39 +47,42 @@ void AddReactionIcon(
button->sizeValue(
) | rpl::start_with_next([=](QSize size) {
icon->moveToLeft(
st::settingsSectionIconLeft,
st::editPeerReactionsIconLeft,
(size.height() - icon->height()) / 2,
size.width());
}, icon->lifetime());
const auto setImage = [=](not_null<Image*> image) {
state->image = Images::prepare(
image->original(),
size * style::DevicePixelRatio(),
size * style::DevicePixelRatio(),
Images::Option::Smooth | Images::Option::TransparentBackground,
size,
size);
icon->update();
const auto initLottie = [=] {
state->icon = std::make_unique<Lottie::Icon>(Lottie::IconDescriptor{
.path = state->media->owner()->filepath(true),
.json = state->media->bytes(),
.sizeOverride = QSize(size, size),
.frame = -1,
});
state->media = nullptr;
};
if (const auto image = state->media->getStickerLarge()) {
setImage(image);
state->media->checkStickerLarge();
if (state->media->loaded()) {
initLottie();
} else {
document->session().downloaderTaskFinished(
) | rpl::map([=] {
return state->media->getStickerLarge();
}) | rpl::filter_nullptr() | rpl::take(
1
) | rpl::start_with_next([=](not_null<Image*> image) {
setImage(image);
}, button->lifetime());
) | rpl::filter([=] {
return state->media->loaded();
}) | rpl::take(1) | rpl::start_with_next([=] {
initLottie();
icon->update();
}, icon->lifetime());
}
icon->paintRequest(
) | rpl::start_with_next([=] {
Painter p(icon);
QPainter p(icon);
if (state->image.isNull() && state->icon) {
state->image = state->icon->frame();
crl::async([icon = std::move(state->icon)]{});
}
if (!state->image.isNull()) {
p.drawImage(0, 0, state->image);
p.drawImage(QRect(0, 0, size, size), state->image);
}
}, icon->lifetime());
}
@@ -88,7 +93,7 @@ void EditAllowedReactionsBox(
not_null<Ui::GenericBox*> box,
bool isGroup,
const std::vector<Reaction> &list,
const std::vector<Reaction> &selected,
const base::flat_set<QString> &selected,
Fn<void(const std::vector<QString> &)> callback) {
box->setTitle(tr::lng_manage_peer_reactions());
@@ -141,14 +146,16 @@ void EditAllowedReactionsBox(
tr::lng_manage_peer_reactions_available());
const auto active = [&](const Data::Reaction &entry) {
return ranges::contains(selected, entry.emoji, &Reaction::emoji);
return selected.contains(entry.emoji);
};
const auto add = [&](const Data::Reaction &entry) {
const auto button = Settings::AddButton(
container,
rpl::single(entry.title),
st::manageGroupButton.button);
AddReactionIcon(button, entry.staticIcon);
AddReactionIcon(button, entry.centerIcon
? entry.centerIcon
: entry.appearAnimation.get());
state->toggles.emplace(entry.emoji, button);
button->toggleOn(rpl::single(
active(entry)
@@ -191,9 +198,9 @@ void SaveAllowedReactions(
)).done([=](const MTPUpdates &result) {
peer->session().api().applyUpdates(result);
if (const auto chat = peer->asChat()) {
chat->setAllowedReactions(allowed);
chat->setAllowedReactions({ begin(allowed), end(allowed) });
} else if (const auto channel = peer->asChannel()) {
channel->setAllowedReactions(allowed);
channel->setAllowedReactions({ begin(allowed), end(allowed) });
} else {
Unexpected("Invalid peer type in SaveAllowedReactions.");
}

View File

@@ -19,7 +19,7 @@ void EditAllowedReactionsBox(
not_null<Ui::GenericBox*> box,
bool isGroup,
const std::vector<Data::Reaction> &list,
const std::vector<Data::Reaction> &selected,
const base::flat_set<QString> &selected,
Fn<void(const std::vector<QString> &)> callback);
void SaveAllowedReactions(

View File

@@ -183,11 +183,10 @@ void PeerShortInfoCover::paint(QPainter &p) {
_widget->size() * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::black);
Images::prepareRound(
image,
_userpicImage = Images::Round(
std::move(image),
ImageRoundRadius::Small,
RectPart::TopLeft | RectPart::TopRight);
_userpicImage = std::move(image);
}
paintCoverImage(p, frame.isNull() ? _userpicImage : frame);
@@ -229,8 +228,8 @@ void PeerShortInfoCover::paintCoverImage(QPainter &p, const QImage &image) {
image,
QRect(0, from * factor, roundedWidth * factor, rounded * factor));
q.end();
Images::prepareRound(
_roundedTopImage,
_roundedTopImage = Images::Round(
std::move(_roundedTopImage),
ImageRoundRadius::Small,
RectPart::TopLeft | RectPart::TopRight);
p.drawImage(
@@ -244,9 +243,8 @@ void PeerShortInfoCover::paintBars(QPainter &p) {
const auto factor = style::DevicePixelRatio();
if (_shadowTop.isNull()) {
_shadowTop = Images::GenerateShadow(height, kShadowMaxAlpha, 0);
_shadowTop = _shadowTop.scaled(QSize(_st.size, height) * factor);
Images::prepareRound(
_shadowTop,
_shadowTop = Images::Round(
_shadowTop.scaled(QSize(_st.size, height) * factor),
ImageRoundRadius::Small,
RectPart::TopLeft | RectPart::TopRight);
}
@@ -771,8 +769,8 @@ int PeerShortInfoBox::fillRoundedTopHeight() {
void PeerShortInfoBox::refreshRoundedTopImage(const QColor &color) {
_roundedTopColor = color;
_roundedTop.fill(color);
Images::prepareRound(
_roundedTop,
_roundedTop = Images::Round(
std::move(_roundedTop),
ImageRoundRadius::Small,
RectPart::TopLeft | RectPart::TopRight);
}

View File

@@ -51,19 +51,15 @@ void GenerateImage(
bool blurred = false) {
using namespace Images;
const auto size = st::shortInfoWidth;
const auto factor = style::DevicePixelRatio();
const auto options = Option::Smooth
| Option::RoundedSmall
| Option::RoundedTopLeft
| Option::RoundedTopRight
| (blurred ? Option::Blurred : Option());
state->current.photo = Images::prepare(
const auto ratio = style::DevicePixelRatio();
const auto options = Option::RoundSmall
| Option::RoundSkipBottomLeft
| Option::RoundSkipBottomRight
| (blurred ? Option::Blur : Option());
state->current.photo = Images::Prepare(
std::move(image),
size * factor,
size * factor,
options,
size,
size);
QSize(size, size) * ratio,
{ .options = options, .outer = { size, size } });
}
void GenerateImage(

View File

@@ -289,12 +289,9 @@ void RenameBox(not_null<Ui::GenericBox*> box) {
Unexpected("Type in LottieForType.");
}();
const auto size = st::sessionBigLottieSize;
static const auto kWhite = style::owned_color(Qt::white);
return std::make_unique<Lottie::Icon>(Lottie::IconDescriptor{
.path = u":/icons/settings/devices/"_q + path + u".lottie"_q,
.color = kWhite.color(),
.sizeOverride = QSize(size, size),
.frame = 1,
});
}
@@ -360,7 +357,7 @@ void RenameBox(not_null<Ui::GenericBox*> box) {
state->lottie->animate(
[=] { result->update(); },
0,
state->lottie->framesCount());
state->lottie->framesCount() - 1);
}, result->lifetime());
}

View File

@@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lottie/lottie_multi_player.h"
#include "lottie/lottie_animation.h"
#include "chat_helpers/stickers_lottie.h"
#include "media/clip/media_clip_reader.h"
#include "window/window_session_controller.h"
#include "base/unixtime.h"
#include "main/main_session.h"
@@ -51,6 +52,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace {
constexpr auto kStickersPanelPerRow = 5;
constexpr auto kMinRepaintDelay = crl::time(33);
constexpr auto kMinAfterScrollDelay = crl::time(33);
using Data::StickersSet;
using Data::StickersPack;
@@ -99,7 +102,8 @@ private:
struct Element {
not_null<DocumentData*> document;
std::shared_ptr<Data::DocumentMedia> documentMedia;
Lottie::Animation *animated = nullptr;
Lottie::Animation *lottie = nullptr;
Media::Clip::ReaderPointer webm;
Ui::Animations::Simple overAnimation;
};
@@ -107,8 +111,18 @@ private:
QSize boundingBoxSize() const;
void paintSticker(Painter &p, int index, QPoint position) const;
void paintSticker(
Painter &p,
int index,
QPoint position,
bool paused,
crl::time now) const;
void setupLottie(int index);
void setupWebm(int index);
void clipCallback(
Media::Clip::Notification notification,
not_null<DocumentData*> document,
int index);
void updateSelected();
void setSelected(int selected);
@@ -123,6 +137,8 @@ private:
not_null<Lottie::MultiPlayer*> getLottiePlayer();
void showPreview();
void updateItems();
void repaintItems(crl::time now = 0);
not_null<Window::SessionController*> _controller;
MTP::Sender _api;
@@ -142,6 +158,12 @@ private:
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
int _visibleTop = 0;
int _visibleBottom = 0;
crl::time _lastScrolledAt = 0;
crl::time _lastUpdatedAt = 0;
base::Timer _updateItemsTimer;
StickerSetIdentifier _input;
mtpRequestId _installRequest = 0;
@@ -370,9 +392,12 @@ StickerSetBox::Inner::Inner(
, _pathGradient(std::make_unique<Ui::PathShiftGradient>(
st::windowBgRipple,
st::windowBgOver,
[=] { update(); }))
[=] { repaintItems(); }))
, _updateItemsTimer([=] { updateItems(); })
, _input(set)
, _previewTimer([=] { showPreview(); }) {
setAttribute(Qt::WA_OpaquePaintEvent);
_api.request(MTPmessages_GetStickerSet(
Data::InputStickerSet(_input),
MTP_int(0) // hash
@@ -387,7 +412,7 @@ StickerSetBox::Inner::Inner(
_controller->session().downloaderTaskFinished(
) | rpl::start_with_next([=] {
update();
updateItems();
}, lifetime());
setMouseTracking(true);
@@ -735,7 +760,7 @@ not_null<Lottie::MultiPlayer*> StickerSetBox::Inner::getLottiePlayer() {
Lottie::MakeFrameRenderer());
_lottiePlayer->updates(
) | rpl::start_with_next([=] {
update();
updateItems();
}, lifetime());
}
return _lottiePlayer.get();
@@ -756,6 +781,7 @@ int32 StickerSetBox::Inner::stickerFromGlobalPos(const QPoint &p) const {
void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), st::boxBg);
if (_elements.empty()) {
return;
}
@@ -764,6 +790,9 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
_pathGradient->startFrame(0, width(), width() / 2);
const auto now = crl::now();
const auto paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer);
for (int32 i = from; i < to; ++i) {
for (int32 j = 0; j < kStickersPanelPerRow; ++j) {
int32 index = i * kStickersPanelPerRow + j;
@@ -771,16 +800,12 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
break;
}
const auto pos = QPoint(st::stickersPadding.left() + j * st::stickersSize.width(), st::stickersPadding.top() + i * st::stickersSize.height());
paintSticker(p, index, pos);
paintSticker(p, index, pos, paused, now);
}
}
if (_lottiePlayer) {
const auto paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer);
if (!paused) {
_lottiePlayer->markFrameShown();
}
if (_lottiePlayer && !paused) {
_lottiePlayer->markFrameShown();
}
}
@@ -793,6 +818,12 @@ QSize StickerSetBox::Inner::boundingBoxSize() const {
void StickerSetBox::Inner::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
if (_visibleTop != visibleTop || _visibleBottom != visibleBottom) {
_visibleTop = visibleTop;
_visibleBottom = visibleBottom;
_lastScrolledAt = crl::now();
update();
}
const auto pauseInRows = [&](int fromRow, int tillRow) {
Expects(fromRow <= tillRow);
@@ -802,8 +833,10 @@ void StickerSetBox::Inner::visibleTopBottomUpdated(
if (index >= _elements.size()) {
break;
}
if (const auto animated = _elements[index].animated) {
_lottiePlayer->pause(animated);
if (const auto lottie = _elements[index].lottie) {
_lottiePlayer->pause(lottie);
} else if (auto &webm = _elements[index].webm) {
webm = nullptr;
}
}
}
@@ -834,17 +867,63 @@ void StickerSetBox::Inner::visibleTopBottomUpdated(
void StickerSetBox::Inner::setupLottie(int index) {
auto &element = _elements[index];
element.animated = ChatHelpers::LottieAnimationFromDocument(
element.lottie = ChatHelpers::LottieAnimationFromDocument(
getLottiePlayer(),
element.documentMedia.get(),
ChatHelpers::StickerLottieSize::StickerSet,
boundingBoxSize() * cIntRetinaFactor());
}
void StickerSetBox::Inner::setupWebm(int index) {
auto &element = _elements[index];
const auto document = element.document;
auto callback = [=](Media::Clip::Notification notification) {
clipCallback(notification, document, index);
};
element.webm = Media::Clip::MakeReader(
element.documentMedia->owner()->location(),
element.documentMedia->bytes(),
std::move(callback));
}
void StickerSetBox::Inner::clipCallback(
Media::Clip::Notification notification,
not_null<DocumentData*> document,
int index) {
const auto i = (index < _elements.size()
&& _elements[index].document == document)
? (_elements.begin() + index)
: ranges::find(_elements, document, &Element::document);
if (i == end(_elements)) {
return;
}
using namespace Media::Clip;
switch (notification) {
case Notification::Reinit: {
auto &webm = i->webm;
if (webm->state() == State::Error) {
webm.setBad();
} else if (webm->ready() && !webm->started()) {
const auto size = ChatHelpers::ComputeStickerSize(
i->document,
boundingBoxSize());
webm->start({ .frame = size, .keepAlpha = true });
}
} break;
case Notification::Repaint: break;
}
updateItems();
}
void StickerSetBox::Inner::paintSticker(
Painter &p,
int index,
QPoint position) const {
QPoint position,
bool paused,
crl::time now) const {
if (const auto over = _elements[index].overAnimation.value((index == _selected) ? 1. : 0.)) {
p.setOpacity(over);
auto tl = position;
@@ -856,47 +935,46 @@ void StickerSetBox::Inner::paintSticker(
const auto &element = _elements[index];
const auto document = element.document;
const auto &media = element.documentMedia;
const auto sticker = document->sticker();
media->checkStickerSmall();
const auto isAnimated = document->sticker()->animated;
if (isAnimated
&& !element.animated
&& media->loaded()) {
const_cast<Inner*>(this)->setupLottie(index);
if (media->loaded()) {
if (sticker->isLottie() && !element.lottie) {
const_cast<Inner*>(this)->setupLottie(index);
} else if (sticker->isWebm() && !element.webm) {
const_cast<Inner*>(this)->setupWebm(index);
}
}
auto w = 1;
auto h = 1;
if (isAnimated && !document->dimensions.isEmpty()) {
const auto request = Lottie::FrameRequest{ boundingBoxSize() * cIntRetinaFactor() };
const auto size = request.size(document->dimensions, true) / cIntRetinaFactor();
w = std::max(size.width(), 1);
h = std::max(size.height(), 1);
} else {
auto coef = qMin((st::stickersSize.width() - st::roundRadiusSmall * 2) / float64(document->dimensions.width()), (st::stickersSize.height() - st::roundRadiusSmall * 2) / float64(document->dimensions.height()));
if (coef > 1) coef = 1;
w = std::max(qRound(coef * document->dimensions.width()), 1);
h = std::max(qRound(coef * document->dimensions.height()), 1);
}
QPoint ppos = position + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2);
const auto size = ChatHelpers::ComputeStickerSize(
document,
boundingBoxSize());
const auto ppos = position + QPoint(
(st::stickersSize.width() - size.width()) / 2,
(st::stickersSize.height() - size.height()) / 2);
if (element.animated && element.animated->ready()) {
const auto frame = element.animated->frame();
if (element.lottie && element.lottie->ready()) {
const auto frame = element.lottie->frame();
p.drawImage(
QRect(ppos, frame.size() / cIntRetinaFactor()),
frame);
_lottiePlayer->unpause(element.animated);
_lottiePlayer->unpause(element.lottie);
} else if (element.webm && element.webm->started()) {
p.drawPixmap(ppos, element.webm->current({
.frame = size,
.keepAlpha = true,
}, paused ? 0 : now));
} else if (const auto image = media->getStickerSmall()) {
p.drawPixmapLeft(
ppos,
width(),
image->pix(w, h));
image->pix(size));
} else {
ChatHelpers::PaintStickerThumbnailPath(
p,
media.get(),
QRect(ppos, QSize(w, h)),
QRect(ppos, size),
_pathGradient.get());
}
}
@@ -965,4 +1043,23 @@ void StickerSetBox::Inner::archiveStickers() {
}).send();
}
void StickerSetBox::Inner::updateItems() {
const auto now = crl::now();
const auto delay = std::max(
_lastScrolledAt + kMinAfterScrollDelay - now,
_lastUpdatedAt + kMinRepaintDelay - now);
if (delay <= 0) {
repaintItems(now);
} else if (!_updateItemsTimer.isActive()
|| _updateItemsTimer.remainingTime() > kMinRepaintDelay) {
_updateItemsTimer.callOnce(std::max(delay, kMinRepaintDelay));
}
}
void StickerSetBox::Inner::repaintItems(crl::time now) {
_lastUpdatedAt = now ? now : crl::now();
update();
}
StickerSetBox::Inner::~Inner() = default;

View File

@@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h"
#include "ui/cached_round_corners.h"
#include "window/window_session_controller.h"
#include "media/clip/media_clip_reader.h"
#include "main/main_session.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -143,10 +144,7 @@ private:
int32 count,
const QString &title,
int titleWidth,
bool installed,
bool official,
bool unread,
bool archived,
Data::StickersSetFlags flagsOverride,
bool removed,
int32 pixw,
int32 pixh);
@@ -154,6 +152,10 @@ private:
bool isRecentSet() const;
bool isMasksSet() const;
bool isWebm() const;
bool isInstalled() const;
bool isUnread() const;
bool isArchived() const;
const not_null<StickersSet*> set;
DocumentData *sticker = nullptr;
@@ -162,16 +164,14 @@ private:
int32 count = 0;
QString title;
int titleWidth = 0;
bool installed = false;
bool official = false;
bool unread = false;
bool archived = false;
Data::StickersSetFlags flagsOverride;
bool removed = false;
int32 pixw = 0;
int32 pixh = 0;
anim::value yadd;
std::unique_ptr<Ui::RippleAnimation> ripple;
std::unique_ptr<Lottie::SinglePlayer> lottie;
Media::Clip::ReaderPointer webm;
};
struct MegagroupSet {
inline bool operator==(const MegagroupSet &other) const {
@@ -221,16 +221,26 @@ private:
void setActionSel(int32 actionSel);
float64 aboveShadowOpacity() const;
void validateLottieAnimation(not_null<Row*> row);
void validateWebmAnimation(not_null<Row*> row);
void validateAnimation(not_null<Row*> row);
void updateRowThumbnail(not_null<Row*> row);
void clipCallback(
not_null<Row*> row,
Media::Clip::Notification notification);
void readVisibleSets();
void updateControlsGeometry();
void rebuildAppendSet(not_null<StickersSet*> set, int maxNameWidth);
void fillSetCover(not_null<StickersSet*> set, DocumentData **outSticker, int *outWidth, int *outHeight) const;
int fillSetCount(not_null<StickersSet*> set) const;
QString fillSetTitle(not_null<StickersSet*> set, int maxNameWidth, int *outTitleWidth) const;
void fillSetFlags(not_null<StickersSet*> set, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outArchived);
[[nodiscard]] QString fillSetTitle(
not_null<StickersSet*> set,
int maxNameWidth,
int *outTitleWidth) const;
[[nodiscard]] Data::StickersSetFlags fillSetFlags(
not_null<StickersSet*> set) const;
void rebuildMegagroupSet();
void fixupMegagroupSetAddress();
void handleMegagroupSetAddressChange();
@@ -247,6 +257,7 @@ private:
int32 _rowHeight;
std::vector<std::unique_ptr<Row>> _rows;
std::vector<std::unique_ptr<Row>> _oldRows;
std::vector<crl::time> _shiftingStartTimes;
crl::time _aboveShadowFadeStart = 0;
anim::value _aboveShadowFadeOpacity;
@@ -322,19 +333,18 @@ void StickersBox::CounterWidget::setCounter(int counter) {
auto dummy = QImage(1, 1, QImage::Format_ARGB32_Premultiplied);
Painter p(&dummy);
auto newWidth = 0;
Dialogs::Ui::paintUnreadCount(p, _text, 0, 0, _st, &newWidth);
const auto badge = Dialogs::Ui::PaintUnreadBadge(p, _text, 0, 0, _st);
resize(newWidth, st::stickersFeaturedBadgeSize);
resize(badge.width(), st::stickersFeaturedBadgeSize);
}
void StickersBox::CounterWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
if (!_text.isEmpty()) {
auto unreadRight = rtl() ? 0 : width();
auto unreadTop = 0;
Dialogs::Ui::paintUnreadCount(p, _text, unreadRight, unreadTop, _st);
const auto unreadRight = rtl() ? 0 : width();
const auto unreadTop = 0;
Dialogs::Ui::PaintUnreadBadge(p, _text, unreadRight, unreadTop, _st);
}
}
@@ -547,7 +557,9 @@ void StickersBox::prepare() {
}
setNoContentMargin(true);
_tabs->sectionActivated(
) | rpl::start_with_next(
) | rpl::filter([=] {
return !_ignoreTabActivation;
}) | rpl::start_with_next(
[this] { switchTab(); },
lifetime());
refreshTabs();
@@ -665,12 +677,16 @@ void StickersBox::refreshTabs() {
|| (_tab == &_featured && !_tabIndices.contains(Section::Featured))
|| (_tab == &_masks && !_tabIndices.contains(Section::Masks))) {
switchTab();
} else if (_tab == &_archived) {
_tabs->setActiveSectionFast(_tabIndices.indexOf(Section::Archived));
} else if (_tab == &_featured) {
_tabs->setActiveSectionFast(_tabIndices.indexOf(Section::Featured));
} else if (_tab == &_masks) {
_tabs->setActiveSectionFast(_tabIndices.indexOf(Section::Masks));
} else {
_ignoreTabActivation = true;
_tabs->setActiveSectionFast(_tabIndices.indexOf((_tab == &_archived)
? Section::Archived
: (_tab == &_featured)
? Section::Featured
: (_tab == &_masks)
? Section::Masks
: Section::Installed));
_ignoreTabActivation = false;
}
updateTabsGeometry();
}
@@ -1023,10 +1039,7 @@ StickersBox::Inner::Row::Row(
int32 count,
const QString &title,
int titleWidth,
bool installed,
bool official,
bool unread,
bool archived,
Data::StickersSetFlags flagsOverride,
bool removed,
int32 pixw,
int32 pixh)
@@ -1035,10 +1048,7 @@ StickersBox::Inner::Row::Row(
, count(count)
, title(title)
, titleWidth(titleWidth)
, installed(installed)
, official(official)
, unread(unread)
, archived(archived)
, flagsOverride(flagsOverride)
, removed(removed)
, pixw(pixw)
, pixh(pixh) {
@@ -1055,6 +1065,22 @@ bool StickersBox::Inner::Row::isMasksSet() const {
return (set->flags & SetFlag::Masks);
}
bool StickersBox::Inner::Row::isWebm() const {
return (set->flags & SetFlag::Webm);
}
bool StickersBox::Inner::Row::isInstalled() const {
return (flagsOverride & SetFlag::Installed);
}
bool StickersBox::Inner::Row::isUnread() const {
return (flagsOverride & SetFlag::Unread);
}
bool StickersBox::Inner::Row::isArchived() const {
return (flagsOverride & SetFlag::Archived);
}
StickersBox::Inner::Inner(
QWidget *parent,
not_null<Window::SessionController*> controller,
@@ -1295,7 +1321,7 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> row, int index) {
p.setPen(st::contactsNameFg);
p.drawTextLeft(namex, namey, width(), row->title, row->titleWidth);
if (row->unread) {
if (row->isUnread()) {
p.setPen(Qt::NoPen);
p.setBrush(st::stickersFeaturedUnreadBg);
@@ -1337,22 +1363,17 @@ void StickersBox::Inner::paintRowThumbnail(
row->stickerMedia->thumbnailWanted(origin);
}
}
validateLottieAnimation(row);
if (!row->lottie) {
const auto thumb = row->thumbnailMedia
? row->thumbnailMedia->image()
: row->stickerMedia
? row->stickerMedia->thumbnail()
: nullptr;
if (!thumb) {
return;
}
p.drawPixmapLeft(
left + (st::contactsPhotoSize - row->pixw) / 2,
st::contactsPadding.top() + (st::contactsPhotoSize - row->pixh) / 2,
width(),
thumb->pix(row->pixw, row->pixh));
} else if (row->lottie->ready()) {
validateAnimation(row);
const auto thumb = row->thumbnailMedia
? row->thumbnailMedia->image()
: row->stickerMedia
? row->stickerMedia->thumbnail()
: nullptr;
const auto paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer);
const auto x = left + (st::contactsPhotoSize - row->pixw) / 2;
const auto y = st::contactsPadding.top() + (st::contactsPhotoSize - row->pixh) / 2;
if (row->lottie && row->lottie->ready()) {
const auto frame = row->lottie->frame();
const auto size = frame.size() / cIntRetinaFactor();
p.drawImage(
@@ -1362,17 +1383,30 @@ void StickersBox::Inner::paintRowThumbnail(
size.width(),
size.height()),
frame);
const auto paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer);
if (!paused) {
row->lottie->markFrameShown();
}
} else if (row->webm && row->webm->started()) {
p.drawPixmapLeft(
x,
y,
width(),
row->webm->current(
{ .frame = { row->pixw, row->pixh }, .keepAlpha = true },
paused ? 0 : crl::now()));
} else if (thumb) {
p.drawPixmapLeft(
x,
y,
width(),
thumb->pix(row->pixw, row->pixh));
}
}
void StickersBox::Inner::validateLottieAnimation(not_null<Row*> row) {
if (row->lottie
|| !ChatHelpers::HasLottieThumbnail(
row->set->flags,
row->thumbnailMedia.get(),
row->stickerMedia.get())) {
return;
@@ -1394,6 +1428,51 @@ void StickersBox::Inner::validateLottieAnimation(not_null<Row*> row) {
}, lifetime());
}
void StickersBox::Inner::validateWebmAnimation(not_null<Row*> row) {
if (row->webm
|| !ChatHelpers::HasWebmThumbnail(
row->set->flags,
row->thumbnailMedia.get(),
row->stickerMedia.get())) {
return;
}
auto callback = [=](Media::Clip::Notification notification) {
clipCallback(row, notification);
};
row->webm = ChatHelpers::WebmThumbnail(
row->thumbnailMedia.get(),
row->stickerMedia.get(),
std::move(callback));
}
void StickersBox::Inner::clipCallback(
not_null<Row*> row,
Media::Clip::Notification notification) {
using namespace Media::Clip;
switch (notification) {
case Notification::Reinit: {
if (!row->webm) {
return;
} else if (row->webm->state() == State::Error) {
row->webm.setBad();
} else if (row->webm->ready() && !row->webm->started()) {
row->webm->start({
.frame = { row->pixw, row->pixh },
.keepAlpha = true,
});
}
} break;
case Notification::Repaint: break;
}
updateRowThumbnail(row);
}
void StickersBox::Inner::validateAnimation(not_null<Row*> row) {
validateWebmAnimation(row);
validateLottieAnimation(row);
}
void StickersBox::Inner::updateRowThumbnail(not_null<Row*> row) {
const auto rowTop = [&] {
if (row == _megagroupSelectedSet.get()) {
@@ -1422,7 +1501,7 @@ void StickersBox::Inner::updateRowThumbnail(not_null<Row*> row) {
void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int index) {
auto removeButton = (_isInstalled && !row->removed);
auto rect = relativeButtonRect(removeButton);
if (!_isInstalled && row->installed && !row->archived && !row->removed) {
if (!_isInstalled && row->isInstalled() && !row->isArchived() && !row->removed) {
// Checkbox after installed from Trending or Archived.
int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (rect.width() + st::stickersFeaturedInstalled.width()) / 2);
int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2;
@@ -1509,7 +1588,7 @@ void StickersBox::Inner::setActionDown(int newActionDown) {
auto rippleMask = Ui::RippleAnimation::ellipseMask(QSize(rippleSize, rippleSize));
ensureRipple(st::stickersRemove.ripple, std::move(rippleMask), removeButton);
}
} else if (!row->installed || row->archived || row->removed) {
} else if (!row->isInstalled() || row->isArchived() || row->removed) {
auto rippleSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height);
auto rippleMask = Ui::RippleAnimation::roundRectMask(rippleSize, st::roundRadiusSmall);
ensureRipple(st::stickersTrendingAdd.ripple, std::move(rippleMask), removeButton);
@@ -1642,7 +1721,7 @@ void StickersBox::Inner::updateSelected() {
selected = selectedIndex;
local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight);
const auto row = _rows[selectedIndex].get();
if (!_megagroupSet && (_isInstalled || !row->installed || row->archived || row->removed)) {
if (!_megagroupSet && (_isInstalled || !row->isInstalled() || row->isArchived() || row->removed)) {
auto removeButton = (_isInstalled && !row->removed);
auto rect = myrtlrect(relativeButtonRect(removeButton));
actionSel = rect.contains(local) ? selectedIndex : -1;
@@ -1945,8 +2024,10 @@ void StickersBox::Inner::rebuildMegagroupSet() {
auto sticker = (DocumentData*)nullptr;
auto pixw = 0, pixh = 0;
fillSetCover(set, &sticker, &pixw, &pixh);
auto installed = true, official = false, unread = false, archived = false, removed = false;
if (!_megagroupSelectedSet || _megagroupSelectedSet->set->id != set->id) {
auto flagsOverride = SetFlag::Installed;
auto removed = false;
if (!_megagroupSelectedSet
|| _megagroupSelectedSet->set->id != set->id) {
_megagroupSetField->setText(set->shortName);
_megagroupSetField->finishAnimating();
}
@@ -1956,10 +2037,7 @@ void StickersBox::Inner::rebuildMegagroupSet() {
count,
title,
titleWidth,
installed,
official,
unread,
archived,
flagsOverride,
removed,
pixw,
pixh);
@@ -1987,6 +2065,7 @@ void StickersBox::Inner::rebuild(bool masks) {
auto maxNameWidth = countMaxNameWidth();
_oldRows = std::move(_rows);
clear();
const auto &order = ([&]() -> const StickersSetsOrder & {
if (_section == Section::Installed) {
@@ -2038,6 +2117,7 @@ void StickersBox::Inner::rebuild(bool masks) {
set->accessHash);
}
}
_oldRows.clear();
session().api().requestStickerSets();
updateSize();
}
@@ -2086,13 +2166,14 @@ void StickersBox::Inner::updateRows() {
}
}
if (!row->isRecentSet()) {
auto wasInstalled = row->installed;
auto wasArchived = row->archived;
fillSetFlags(set, &row->installed, &row->official, &row->unread, &row->archived);
auto wasInstalled = row->isInstalled();
auto wasArchived = row->isArchived();
row->flagsOverride = fillSetFlags(set);
if (_isInstalled) {
row->archived = false;
row->flagsOverride &= ~SetFlag::Archived;
}
if (row->installed != wasInstalled || row->archived != wasArchived) {
if (row->isInstalled() != wasInstalled
|| row->isArchived() != wasArchived) {
row->ripple.reset();
}
}
@@ -2134,11 +2215,11 @@ int StickersBox::Inner::countMaxNameWidth() const {
void StickersBox::Inner::rebuildAppendSet(
not_null<StickersSet*> set,
int maxNameWidth) {
bool installed = true, official = true, unread = false, archived = false, removed = false;
if (set->id != Data::Stickers::CloudRecentSetId) {
fillSetFlags(set, &installed, &official, &unread, &archived);
}
if (_isInstalled && archived) {
auto flagsOverride = (set->id != Data::Stickers::CloudRecentSetId)
? fillSetFlags(set)
: SetFlag::Installed;
auto removed = false;
if (_isInstalled && (flagsOverride & SetFlag::Archived)) {
return;
}
@@ -2150,19 +2231,49 @@ void StickersBox::Inner::rebuildAppendSet(
QString title = fillSetTitle(set, maxNameWidth, &titleWidth);
int count = fillSetCount(set);
_rows.push_back(std::make_unique<Row>(
set,
sticker,
count,
title,
titleWidth,
installed,
official,
unread,
archived,
removed,
pixw,
pixh));
const auto existing = [&]{
const auto now = int(_rows.size());
const auto setProj = [](const std::unique_ptr<Row> &row) {
return row ? row->set.get() : nullptr;
};
if (_oldRows.size() > now
&& setProj(_oldRows[now]) == set.get()) {
return _oldRows.begin() + now;
}
return ranges::find(_oldRows, set.get(), setProj);
}();
if (existing != end(_oldRows)) {
const auto raw = existing->get();
raw->sticker = sticker;
raw->count = count;
raw->title = title;
raw->titleWidth = titleWidth;
raw->flagsOverride = flagsOverride;
raw->removed = removed;
raw->pixw = pixw;
raw->pixh = pixh;
raw->yadd = {};
auto oldStickerMedia = std::move(raw->stickerMedia);
auto oldThumbnailMedia = std::move(raw->thumbnailMedia);
raw->stickerMedia = sticker ? sticker->activeMediaView() : nullptr;
raw->thumbnailMedia = set->activeThumbnailView();
if (raw->thumbnailMedia != oldThumbnailMedia
|| (!raw->thumbnailMedia && raw->stickerMedia != oldStickerMedia)) {
raw->lottie = nullptr;
}
_rows.push_back(std::move(*existing));
} else {
_rows.push_back(std::make_unique<Row>(
set,
sticker,
count,
title,
titleWidth,
flagsOverride,
removed,
pixw,
pixh));
}
_shiftingStartTimes.push_back(0);
}
@@ -2244,20 +2355,12 @@ QString StickersBox::Inner::fillSetTitle(
return result;
}
void StickersBox::Inner::fillSetFlags(
not_null<StickersSet*> set,
bool *outInstalled,
bool *outOfficial,
bool *outUnread,
bool *outArchived) {
*outInstalled = (set->flags & SetFlag::Installed);
*outOfficial = (set->flags & SetFlag::Official);
*outArchived = (set->flags & SetFlag::Archived);
if (_section == Section::Featured) {
*outUnread = (set->flags & SetFlag::Unread);
} else {
*outUnread = false;
}
Data::StickersSetFlags StickersBox::Inner::fillSetFlags(
not_null<StickersSet*> set) const {
const auto result = set->flags;
return (_section == Section::Featured)
? result
: (result & ~SetFlag::Unread);
}
template <typename Check>
@@ -2274,7 +2377,7 @@ StickersSetsOrder StickersBox::Inner::collectSets(Check check) const {
StickersSetsOrder StickersBox::Inner::getOrder() const {
return collectSets([](Row *row) {
return !row->archived && !row->removed && !row->isRecentSet();
return !row->isArchived() && !row->removed && !row->isRecentSet();
});
}
@@ -2349,7 +2452,7 @@ void StickersBox::Inner::readVisibleSets() {
int rowTo = ceilclamp(itemsVisibleBottom, _rowHeight, 0, _rows.size());
for (int i = rowFrom; i < rowTo; ++i) {
const auto row = _rows[i].get();
if (!row->unread) {
if (!row->isUnread()) {
continue;
}
if ((i * _rowHeight < itemsVisibleTop)

View File

@@ -143,6 +143,7 @@ private:
object_ptr<Ui::SettingsSlider> _tabs = { nullptr };
QList<Section> _tabIndices;
bool _ignoreTabActivation = false;
class CounterWidget;
object_ptr<CounterWidget> _unreadBadge = { nullptr };

View File

@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_user.h"
#include "core/click_handler_types.h"
#include "ui/text/text_utilities.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
@@ -255,11 +256,11 @@ not_null<Ui::RpWidget*> UrlAuthBox::setupContent(
tr::lng_url_auth_open_confirm(tr::now, lt_link, url),
st::boxLabel),
st::boxPadding);
const auto addCheckbox = [&](const QString &text) {
const auto addCheckbox = [&](const TextWithEntities &text) {
const auto checkbox = result->add(
object_ptr<Ui::Checkbox>(
result,
QString(),
text,
true,
st::urlAuthCheckbox),
style::margins(
@@ -268,23 +269,22 @@ not_null<Ui::RpWidget*> UrlAuthBox::setupContent(
st::boxPadding.right(),
st::boxPadding.bottom()));
checkbox->setAllowTextLines();
checkbox->setText(text, true);
return checkbox;
};
const auto auth = addCheckbox(
tr::lng_url_auth_login_option(
tr::now,
lt_domain,
textcmdStartSemibold() + domain + textcmdStopSemibold(),
Ui::Text::Bold(domain),
lt_user,
(textcmdStartSemibold()
+ session->user()->name
+ textcmdStopSemibold())));
Ui::Text::Bold(session->user()->name),
Ui::Text::WithEntities));
const auto allow = bot
? addCheckbox(tr::lng_url_auth_allow_messages(
tr::now,
lt_bot,
textcmdStartSemibold() + bot->firstName + textcmdStopSemibold()))
Ui::Text::Bold(bot->firstName),
Ui::Text::WithEntities))
: nullptr;
if (allow) {
rpl::single(

View File

@@ -411,49 +411,45 @@ callBarSignalBars: CallSignalBars(callPanelSignalBars) {
color: callBarFg;
}
callTitleButton: IconButton {
width: 34px;
height: 30px;
iconPosition: point(0px, 0px);
}
callTitleButton: windowTitleButton;
callTitleMinimizeIcon: icon {
{ "calls/calls_minimize_shadow", windowShadowFg },
{ "calls/calls_minimize_main", callNameFg },
{ "title_shadow_minimize", windowShadowFg },
{ "title_button_minimize", callNameFg },
};
callTitleMinimizeIconOver: icon {
{ size(34px, 30px), callBgButton },
{ size(34px, 30px), callMuteRipple },
{ "calls/calls_minimize_shadow", windowShadowFg },
{ "calls/calls_minimize_main", callNameFg },
{ windowTitleButtonSize, callBgButton },
{ windowTitleButtonSize, callMuteRipple },
{ "title_shadow_minimize", windowShadowFg },
{ "title_button_minimize", callNameFg },
};
callTitleMaximizeIcon: icon {
{ "calls/calls_maximize_shadow", windowShadowFg },
{ "calls/calls_maximize_main", callNameFg },
{ "title_shadow_maximize", windowShadowFg },
{ "title_button_maximize", callNameFg },
};
callTitleMaximizeIconOver: icon {
{ size(34px, 30px), callBgButton },
{ size(34px, 30px), callMuteRipple },
{ "calls/calls_maximize_shadow", windowShadowFg },
{ "calls/calls_maximize_main", callNameFg },
{ windowTitleButtonSize, callBgButton },
{ windowTitleButtonSize, callMuteRipple },
{ "title_shadow_maximize", windowShadowFg },
{ "title_button_maximize", callNameFg },
};
callTitleRestoreIcon: icon {
{ "calls/calls_restore_shadow", windowShadowFg },
{ "calls/calls_restore_main", callNameFg },
{ "title_shadow_restore", windowShadowFg },
{ "title_button_restore", callNameFg },
};
callTitleRestoreIconOver: icon {
{ size(34px, 30px), callBgButton },
{ size(34px, 30px), callMuteRipple },
{ "calls/calls_restore_shadow", windowShadowFg },
{ "calls/calls_restore_main", callNameFg },
{ windowTitleButtonSize, callBgButton },
{ windowTitleButtonSize, callMuteRipple },
{ "title_shadow_restore", windowShadowFg },
{ "title_button_restore", callNameFg },
};
callTitleCloseIcon: icon {
{ "calls/calls_close_shadow", windowShadowFg },
{ "calls/calls_close_main", callNameFg },
{ "title_shadow_close", windowShadowFg },
{ "title_button_close", callNameFg },
};
callTitleCloseIconOver: icon {
{ size(34px, 30px), titleButtonCloseBgOver },
{ "calls/calls_close_shadow", windowShadowFg },
{ "calls/calls_close_main", titleButtonCloseFgOver },
{ windowTitleButtonSize, titleButtonCloseBgOver },
{ "title_shadow_close", windowShadowFg },
{ "title_button_close", titleButtonCloseFgOver },
};
callTitle: WindowTitle(defaultWindowTitle) {
height: 0px;
@@ -1055,37 +1051,37 @@ groupCallDelaySlider: MediaSlider(defaultContinuousSlider) {
groupCallDelayMargin: margins(22px, 5px, 20px, 10px);
groupCallTitleButton: IconButton {
width: 24px;
height: 21px;
width: windowTitleButtonWidth;
height: windowTitleHeight;
iconPosition: point(0px, 0px);
}
groupCallTitleMinimizeIcon: icon {
{ "title_button_minimize", groupCallMemberNotJoinedStatus, point(4px, 4px) },
{ "title_button_minimize", groupCallMemberNotJoinedStatus },
};
groupCallTitleMinimizeIconOver: icon {
{ size(24px, 21px), groupCallMembersBgOver },
{ "title_button_minimize", groupCallMembersFg, point(4px, 4px) },
{ windowTitleButtonSize, groupCallMembersBgOver },
{ "title_button_minimize", groupCallMembersFg },
};
groupCallTitleMaximizeIcon: icon {
{ "title_button_maximize", groupCallMemberNotJoinedStatus, point(4px, 4px) },
{ "title_button_maximize", groupCallMemberNotJoinedStatus },
};
groupCallTitleMaximizeIconOver: icon {
{ size(24px, 21px), groupCallMembersBgOver },
{ "title_button_maximize", groupCallMembersFg, point(4px, 4px) },
{ windowTitleButtonSize, groupCallMembersBgOver },
{ "title_button_maximize", groupCallMembersFg },
};
groupCallTitleRestoreIcon: icon {
{ "title_button_restore", groupCallMemberNotJoinedStatus, point(4px, 4px) },
{ "title_button_restore", groupCallMemberNotJoinedStatus },
};
groupCallTitleRestoreIconOver: icon {
{ size(24px, 21px), groupCallMembersBgOver },
{ "title_button_restore", groupCallMembersFg, point(4px, 4px) },
{ windowTitleButtonSize, groupCallMembersBgOver },
{ "title_button_restore", groupCallMembersFg },
};
groupCallTitleCloseIcon: icon {
{ "title_button_close", groupCallMemberNotJoinedStatus, point(4px, 4px) },
{ "title_button_close", groupCallMemberNotJoinedStatus },
};
groupCallTitleCloseIconOver: icon {
{ size(24px, 21px), titleButtonCloseBgOver },
{ "title_button_close", titleButtonCloseFgOver, point(4px, 4px) },
{ windowTitleButtonSize, titleButtonCloseBgOver },
{ "title_button_close", titleButtonCloseFgOver },
};
groupCallTitle: WindowTitle(defaultWindowTitle) {
height: 0px;
@@ -1199,7 +1195,7 @@ desktopCaptureSourceSkips: size(2px, 10px);
desktopCaptureSourceTitle: WindowTitle(groupCallTitle) {
bg: groupCallMembersBgOver;
bgActive: groupCallMembersBgOver;
height: 21px;
height: windowTitleHeight;
}
desktopCapturePadding: margins(7px, 7px, 7px, 33px);
desktopCaptureLabelBottom: 7px;

View File

@@ -32,7 +32,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h"
#include "base/unixtime.h"
#include "mtproto/mtproto_config.h"
#include "app.h" // App::quitting
#include <tgcalls/VideoCaptureInterface.h>
#include <tgcalls/StaticThreads.h>
@@ -247,7 +246,7 @@ void Instance::destroyCall(not_null<Call*> call) {
_currentCallChanges.fire(nullptr);
taken.reset();
if (App::quitting()) {
if (Core::Quitting()) {
LOG(("Calls::Instance doesn't prevent quit any more."));
}
Core::App().quitPreventFinished();
@@ -285,7 +284,7 @@ void Instance::destroyGroupCall(not_null<GroupCall*> call) {
_currentGroupCallChanges.fire(nullptr);
taken.reset();
if (App::quitting()) {
if (Core::Quitting()) {
LOG(("Calls::Instance doesn't prevent quit any more."));
}
Core::App().quitPreventFinished();

View File

@@ -61,8 +61,8 @@ Panel::Panel(not_null<Call*> call)
, _user(call->user())
, _layerBg(std::make_unique<Ui::LayerManager>(widget()))
#ifndef Q_OS_MAC
, _controls(std::make_unique<Ui::Platform::TitleControls>(
widget(),
, _controls(Ui::Platform::SetupSeparateTitleControls(
window(),
st::callTitle,
[=](bool maximized) { toggleFullScreen(maximized); }))
#endif // !Q_OS_MAC
@@ -144,7 +144,7 @@ void Panel::initWindow() {
return Flag::None | Flag(0);
}
#ifndef Q_OS_MAC
if (_controls->geometry().contains(widgetPoint)) {
if (_controls->controls.geometry().contains(widgetPoint)) {
return Flag::None | Flag(0);
}
#endif // !Q_OS_MAC
@@ -550,7 +550,7 @@ void Panel::initLayout() {
}, widget()->lifetime());
#ifndef Q_OS_MAC
_controls->raise();
_controls->wrap.raise();
#endif // !Q_OS_MAC
}
@@ -628,7 +628,7 @@ void Panel::updateControlsGeometry() {
}
if (_fingerprint) {
#ifndef Q_OS_MAC
const auto controlsGeometry = _controls->geometry();
const auto controlsGeometry = _controls->controls.geometry();
const auto halfWidth = widget()->width() / 2;
const auto minLeft = (controlsGeometry.center().x() < halfWidth)
? (controlsGeometry.width() + st::callFingerprintTop)

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/weak_ptr.h"
#include "base/timer.h"
#include "base/object_ptr.h"
#include "base/unique_qptr.h"
#include "calls/calls_call.h"
#include "calls/group/ui/desktop_capture_choose_source.h"
#include "ui/effects/animations.h"
@@ -37,7 +38,7 @@ namespace GL {
enum class Backend;
} // namespace GL
namespace Platform {
class TitleControls;
struct SeparateTitleControls;
} // namespace Platform
} // namespace Ui
@@ -126,7 +127,7 @@ private:
std::unique_ptr<Incoming> _incoming;
#ifndef Q_OS_MAC
std::unique_ptr<Ui::Platform::TitleControls> _controls;
std::unique_ptr<Ui::Platform::SeparateTitleControls> _controls;
#endif // !Q_OS_MAC
QSize _incomingFrameSize;

View File

@@ -165,8 +165,11 @@ void Userpic::refreshPhoto() {
void Userpic::createCache(Image *image) {
const auto size = this->size();
const auto real = size * cIntRetinaFactor();
auto options = Images::Option::Smooth | Images::Option::Circled;
// _useTransparency ? (Images::Option::RoundedLarge | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::Smooth) : Images::Option::None;
//_useTransparency
// ? (Images::Option::RoundLarge
// | Images::Option::RoundSkipBottomLeft
// | Images::Option::RoundSkipBottomRight)
// : Images::Option::None;
if (image) {
auto width = image->width();
auto height = image->height();
@@ -178,14 +181,16 @@ void Userpic::createCache(Image *image) {
width = real;
}
_userPhoto = image->pixNoCache(
width,
height,
options,
size,
size);
{ width, height },
{
.options = Images::Option::RoundCircle,
.outer = { size, size },
});
_userPhoto.setDevicePixelRatio(cRetinaFactor());
} else {
auto filled = QImage(QSize(real, real), QImage::Format_ARGB32_Premultiplied);
auto filled = QImage(
QSize(real, real),
QImage::Format_ARGB32_Premultiplied);
filled.setDevicePixelRatio(cRetinaFactor());
filled.fill(Qt::transparent);
{
@@ -195,7 +200,10 @@ void Userpic::createCache(Image *image) {
_peer->name
).paint(p, 0, 0, size, size);
}
//Images::prepareRound(filled, ImageRoundRadius::Large, RectPart::TopLeft | RectPart::TopRight);
//_userPhoto = Images::PixmapFast(Images::Round(
// std::move(filled),
// ImageRoundRadius::Large,
// RectPart::TopLeft | RectPart::TopRight));
_userPhoto = Images::PixmapFast(std::move(filled));
}

View File

@@ -169,12 +169,12 @@ void VideoBubble::prepareFrame() {
for (; from != till; from += fromPerLine, to += toPerLine) {
memcpy(to, from, lineSize);
}
Images::prepareRound(
_frame,
_frame = Images::Round(
std::move(_frame),
ImageRoundRadius::Large,
RectPart::AllCorners,
QRect(QPoint(), size));
_frame = std::move(_frame).mirrored(true, false);
QRect(QPoint(), size)
).mirrored(true, false);
}
void VideoBubble::setState(Webrtc::VideoState state) {

View File

@@ -2396,6 +2396,7 @@ bool GroupCall::tryCreateScreencast() {
tgcalls::GroupInstanceDescriptor descriptor = {
.threads = tgcalls::StaticThreads::getThreads(),
.config = tgcalls::GroupConfig{
.need_log = Logs::DebugEnabled(),
},
.networkStateUpdated = [=](tgcalls::GroupNetworkState networkState) {
crl::on_main(weak, [=] {

View File

@@ -1195,7 +1195,10 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
const auto admin = IsGroupCallAdmin(_peer, participantPeer);
const auto session = &_peer->session();
const auto getCurrentWindow = [=]() -> Window::SessionController* {
if (const auto window = Core::App().activeWindow()) {
if (const auto window = Core::App().separateWindowForPeer(
participantPeer)) {
return window->sessionController();
} else if (const auto window = Core::App().activeWindow()) {
if (const auto controller = window->sessionController()) {
if (&controller->session() == session) {
return controller;
@@ -1221,7 +1224,7 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
? st::groupCallPopupMenuWithVolume
: st::groupCallPopupMenu));
const auto weakMenu = Ui::MakeWeak(result.get());
const auto performOnMainWindow = [=](auto callback) {
const auto withActiveWindow = [=](auto callback) {
if (const auto window = getWindow()) {
if (const auto menu = weakMenu.data()) {
menu->discardParentReActivate();
@@ -1236,12 +1239,12 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
}
};
const auto showProfile = [=] {
performOnMainWindow([=](not_null<Window::SessionController*> window) {
withActiveWindow([=](not_null<Window::SessionController*> window) {
window->showPeerInfo(participantPeer);
});
};
const auto showHistory = [=] {
performOnMainWindow([=](not_null<Window::SessionController*> window) {
withActiveWindow([=](not_null<Window::SessionController*> window) {
window->showPeerHistory(
participantPeer,
Window::SectionShow::Way::Forward);

View File

@@ -108,7 +108,7 @@ private:
};
TextParseOptions MenuTextOptions = {
TextParseLinks | TextParseRichText, // flags
TextParseLinks, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir

View File

@@ -88,8 +88,8 @@ Panel::Panel(not_null<GroupCall*> call)
, _peer(call->peer())
, _layerBg(std::make_unique<Ui::LayerManager>(widget()))
#ifndef Q_OS_MAC
, _controls(std::make_unique<Ui::Platform::TitleControls>(
widget(),
, _controls(Ui::Platform::SetupSeparateTitleControls(
window(),
st::groupCallTitle))
#endif // !Q_OS_MAC
, _viewport(
@@ -302,7 +302,7 @@ void Panel::initWidget() {
updateControlsGeometry();
}
// title geometry depends on _controls->geometry,
// title geometry depends on _controls->controls.geometry,
// which is not updated here yet.
crl::on_main(widget(), [=] { refreshTitle(); });
}, lifetime());
@@ -1368,7 +1368,7 @@ void Panel::initLayout() {
initGeometry();
#ifndef Q_OS_MAC
_controls->raise();
_controls->wrap.raise();
Ui::Platform::TitleControlsLayoutChanged(
) | rpl::start_with_next([=] {
@@ -1413,7 +1413,7 @@ QRect Panel::computeTitleRect() const {
#ifdef Q_OS_MAC
return QRect(70, 0, width - remove - 70, 28);
#else // Q_OS_MAC
const auto controls = _controls->geometry();
const auto controls = _controls->controls.geometry();
const auto right = controls.x() + controls.width() + skip;
return (controls.center().x() < width / 2)
? QRect(right, 0, width - right - remove, controls.height())
@@ -1884,7 +1884,8 @@ void Panel::updateControlsGeometry() {
#ifdef Q_OS_MAC
const auto controlsOnTheLeft = true;
#else // Q_OS_MAC
const auto controlsOnTheLeft = _controls->geometry().center().x()
const auto center = _controls->controls.geometry().center();
const auto controlsOnTheLeft = center.x()
< widget()->width() / 2;
#endif // Q_OS_MAC
const auto menux = st::groupCallMenuTogglePosition.x();

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h"
#include "base/flags.h"
#include "base/object_ptr.h"
#include "base/unique_qptr.h"
#include "calls/group/calls_group_call.h"
#include "calls/group/calls_group_common.h"
#include "calls/group/calls_choose_join_as.h"
@@ -51,7 +52,7 @@ namespace Toast {
class Instance;
} // namespace Toast
namespace Platform {
class TitleControls;
struct SeparateTitleControls;
} // namespace Platform
} // namespace Ui
@@ -194,7 +195,7 @@ private:
rpl::variable<PanelMode> _mode;
#ifndef Q_OS_MAC
std::unique_ptr<Ui::Platform::TitleControls> _controls;
std::unique_ptr<Ui::Platform::SeparateTitleControls> _controls;
#endif // !Q_OS_MAC
rpl::lifetime _callLifetime;

View File

@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "core/core_settings.h"
#include "lottie/lottie_single_player.h"
#include "media/clip/media_clip_reader.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/input_fields.h"
@@ -37,7 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/cached_round_corners.h"
#include "base/unixtime.h"
#include "base/random.h"
#include "base/qt_adapters.h"
#include "base/qt/qt_common_adapters.h"
#include "window/window_adaptive.h"
#include "window/window_session_controller.h"
#include "styles/style_chat.h"
@@ -102,8 +103,13 @@ private:
QSize stickerBoundingBox() const;
void setupLottie(StickerSuggestion &suggestion);
void setupWebm(StickerSuggestion &suggestion);
void repaintSticker(not_null<DocumentData*> document);
void repaintStickerAtIndex(int index);
std::shared_ptr<Lottie::FrameRenderer> getLottieRenderer();
void clipCallback(
Media::Clip::Notification notification,
not_null<DocumentData*> document);
const not_null<Window::SessionController*> _controller;
const not_null<FieldAutocomplete*> _parent;
@@ -141,6 +147,13 @@ private:
};
struct FieldAutocomplete::StickerSuggestion {
not_null<DocumentData*> document;
std::shared_ptr<Data::DocumentMedia> documentMedia;
std::unique_ptr<Lottie::SinglePlayer> lottie;
Media::Clip::ReaderPointer webm;
};
FieldAutocomplete::FieldAutocomplete(
QWidget *parent,
not_null<Window::SessionController*> controller)
@@ -340,7 +353,7 @@ FieldAutocomplete::StickerRows FieldAutocomplete::getStickerSuggestions() {
};
}) | ranges::to_vector;
for (auto &suggestion : _srows) {
if (!suggestion.animated) {
if (!suggestion.lottie && !suggestion.webm) {
continue;
}
const auto i = ranges::find(
@@ -348,7 +361,8 @@ FieldAutocomplete::StickerRows FieldAutocomplete::getStickerSuggestions() {
suggestion.document,
&StickerSuggestion::document);
if (i != end(result)) {
i->animated = std::move(suggestion.animated);
i->lottie = std::move(suggestion.lottie);
i->webm = std::move(suggestion.webm);
}
}
return result;
@@ -812,6 +826,7 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
width(),
std::min(st::msgMaxWidth / 2, width() / 2));
const auto now = crl::now();
int32 rows = rowscount(_srows->size(), _stickersPerRow);
int32 fromrow = floorclamp(r.y() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows);
int32 torow = ceilclamp(r.y() + r.height() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows);
@@ -825,12 +840,15 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
auto &sticker = (*_srows)[index];
const auto document = sticker.document;
const auto &media = sticker.documentMedia;
if (!document->sticker()) continue;
const auto info = document->sticker();
if (!info) continue;
if (document->sticker()->animated
&& !sticker.animated
&& media->loaded()) {
setupLottie(sticker);
if (media->loaded()) {
if (info->isLottie() && !sticker.lottie) {
setupLottie(sticker);
} else if (info->isWebm() && !sticker.webm) {
setupWebm(sticker);
}
}
QPoint pos(st::stickerPanPadding + col * st::stickerPanSize.width(), st::stickerPanPadding + row * st::stickerPanSize.height());
@@ -841,46 +859,34 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
}
media->checkStickerSmall();
auto w = 1;
auto h = 1;
if (sticker.animated && !document->dimensions.isEmpty()) {
const auto request = Lottie::FrameRequest{ stickerBoundingBox() * cIntRetinaFactor() };
const auto size = request.size(document->dimensions, true) / cIntRetinaFactor();
w = std::max(size.width(), 1);
h = std::max(size.height(), 1);
} else {
const auto coef = std::min(
std::min(
(st::stickerPanSize.width() - st::roundRadiusSmall * 2) / float64(document->dimensions.width()),
(st::stickerPanSize.height() - st::roundRadiusSmall * 2) / float64(document->dimensions.height())),
1.);
w = std::max(qRound(coef * document->dimensions.width()), 1);
h = std::max(qRound(coef * document->dimensions.height()), 1);
}
if (sticker.animated && sticker.animated->ready()) {
const auto frame = sticker.animated->frame();
const auto size = frame.size() / cIntRetinaFactor();
const auto ppos = pos + QPoint(
(st::stickerPanSize.width() - size.width()) / 2,
(st::stickerPanSize.height() - size.height()) / 2);
const auto paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::SavedGifs);
const auto size = ChatHelpers::ComputeStickerSize(
document,
stickerBoundingBox());
const auto ppos = pos + QPoint(
(st::stickerPanSize.width() - size.width()) / 2,
(st::stickerPanSize.height() - size.height()) / 2);
if (sticker.lottie && sticker.lottie->ready()) {
const auto frame = sticker.lottie->frame();
p.drawImage(
QRect(ppos, size),
QRect(ppos, frame.size() / cIntRetinaFactor()),
frame);
const auto paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::SavedGifs);
if (!paused) {
sticker.animated->markFrameShown();
sticker.lottie->markFrameShown();
}
} else if (sticker.webm && sticker.webm->started()) {
p.drawPixmap(ppos, sticker.webm->current({
.frame = size,
.keepAlpha = true,
}, paused ? 0 : now));
} else if (const auto image = media->getStickerSmall()) {
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
p.drawPixmapLeft(ppos, width(), image->pix(w, h));
p.drawPixmapLeft(ppos, width(), image->pix(size));
} else {
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
ChatHelpers::PaintStickerThumbnailPath(
p,
media.get(),
QRect(ppos, QSize(w, h)),
QRect(ppos, size),
_pathGradient.get());
}
}
@@ -1271,19 +1277,30 @@ auto FieldAutocomplete::Inner::getLottieRenderer()
void FieldAutocomplete::Inner::setupLottie(StickerSuggestion &suggestion) {
const auto document = suggestion.document;
suggestion.animated = ChatHelpers::LottiePlayerFromDocument(
suggestion.lottie = ChatHelpers::LottiePlayerFromDocument(
suggestion.documentMedia.get(),
ChatHelpers::StickerLottieSize::InlineResults,
stickerBoundingBox() * cIntRetinaFactor(),
Lottie::Quality::Default,
getLottieRenderer());
suggestion.animated->updates(
suggestion.lottie->updates(
) | rpl::start_with_next([=] {
repaintSticker(document);
}, _stickersLifetime);
}
void FieldAutocomplete::Inner::setupWebm(StickerSuggestion &suggestion) {
const auto document = suggestion.document;
auto callback = [=](Media::Clip::Notification notification) {
clipCallback(notification, document);
};
suggestion.webm = Media::Clip::MakeReader(
suggestion.documentMedia->owner()->location(),
suggestion.documentMedia->bytes(),
std::move(callback));
}
QSize FieldAutocomplete::Inner::stickerBoundingBox() const {
return QSize(
st::stickerPanSize.width() - st::roundRadiusSmall * 2,
@@ -1299,7 +1316,10 @@ void FieldAutocomplete::Inner::repaintSticker(
if (i == end(*_srows)) {
return;
}
const auto index = (i - begin(*_srows));
repaintStickerAtIndex(i - begin(*_srows));
}
void FieldAutocomplete::Inner::repaintStickerAtIndex(int index) {
const auto row = (index / _stickersPerRow);
const auto col = (index % _stickersPerRow);
update(
@@ -1309,6 +1329,36 @@ void FieldAutocomplete::Inner::repaintSticker(
st::stickerPanSize.height());
}
void FieldAutocomplete::Inner::clipCallback(
Media::Clip::Notification notification,
not_null<DocumentData*> document) {
const auto i = ranges::find(
*_srows,
document,
&StickerSuggestion::document);
if (i == end(*_srows)) {
return;
}
using namespace Media::Clip;
switch (notification) {
case Notification::Reinit: {
if (!i->webm) {
break;
} else if (i->webm->state() == State::Error) {
i->webm.setBad();
} else if (i->webm->ready() && !i->webm->started()) {
const auto size = ChatHelpers::ComputeStickerSize(
i->document,
stickerBoundingBox());
i->webm->start({ .frame = size, .keepAlpha = true });
}
} break;
case Notification::Repaint: break;
}
repaintStickerAtIndex(i - begin(*_srows));
}
void FieldAutocomplete::Inner::selectByMouse(QPoint globalPosition) {
_mouseSelection = true;
_lastMousePosition = globalPosition;

View File

@@ -129,12 +129,7 @@ protected:
private:
class Inner;
friend class Inner;
struct StickerSuggestion {
not_null<DocumentData*> document;
std::shared_ptr<Data::DocumentMedia> documentMedia;
std::unique_ptr<Lottie::SinglePlayer> animated;
};
struct StickerSuggestion;
struct MentionRow {
not_null<UserData*> user;

View File

@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_toggling_media.h" // Api::ToggleSavedGif
#include "base/const_string.h"
#include "base/qt/qt_key_modifiers.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "data/data_session.h"
@@ -45,6 +46,8 @@ namespace {
constexpr auto kSearchRequestDelay = 400;
constexpr auto kInlineItemsMaxPerRow = 5;
constexpr auto kSearchBotUsername = "gif"_cs;
constexpr auto kMinRepaintDelay = crl::time(33);
constexpr auto kMinAfterScrollDelay = crl::time(33);
} // namespace
@@ -188,14 +191,14 @@ GifsListWidget::GifsListWidget(
controller->session().downloaderTaskFinished(
) | rpl::start_with_next([=] {
update();
updateInlineItems();
}, lifetime());
controller->gifPauseLevelChanged(
) | rpl::start_with_next([=] {
if (!controller->isGifPausedAtLeastFor(
Window::GifPauseReason::SavedGifs)) {
update();
updateInlineItems();
}
}, lifetime());
@@ -235,10 +238,11 @@ object_ptr<TabbedSelector::InnerFooter> GifsListWidget::createFooter() {
void GifsListWidget::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
auto top = getVisibleTop();
const auto top = getVisibleTop();
Inner::visibleTopBottomUpdated(visibleTop, visibleBottom);
if (top != getVisibleTop()) {
_lastScrolled = crl::now();
_lastScrolledAt = crl::now();
update();
}
checkLoadMore();
}
@@ -437,8 +441,7 @@ void GifsListWidget::selectInlineResult(
return;
}
forceSend |= (QGuiApplication::keyboardModifiers()
== Qt::ControlModifier);
forceSend |= base::IsCtrlPressed();
if (const auto photo = item->getPhoto()) {
using Data::PhotoSize;
const auto media = photo->activeMediaView();
@@ -498,7 +501,7 @@ void GifsListWidget::clearSelection() {
setCursor(style::cur_default);
}
_selected = _pressed = -1;
update();
repaintItems();
}
TabbedSelector::InnerFooter *GifsListWidget::getFooter() const {
@@ -544,7 +547,7 @@ void GifsListWidget::refreshSavedGifs() {
deleteUnusedGifLayouts();
resizeToWidth(width());
update();
repaintItems();
}
if (isVisible()) {
@@ -672,7 +675,7 @@ int GifsListWidget::refreshInlineRows(const InlineCacheEntry *entry, bool result
}
resizeToWidth(width());
update();
repaintItems();
_lastMousePos = QCursor::pos();
updateSelected();
@@ -711,16 +714,13 @@ void GifsListWidget::inlineItemLayoutChanged(const InlineBots::Layout::ItemBase
}
}
void GifsListWidget::inlineItemRepaint(const InlineBots::Layout::ItemBase *layout) {
auto ms = crl::now();
if (_lastScrolled + 100 <= ms) {
update();
} else {
_updateInlineItems.callOnce(_lastScrolled + 100 - ms);
}
void GifsListWidget::inlineItemRepaint(
const InlineBots::Layout::ItemBase *layout) {
updateInlineItems();
}
bool GifsListWidget::inlineItemVisible(const InlineBots::Layout::ItemBase *layout) {
bool GifsListWidget::inlineItemVisible(
const InlineBots::Layout::ItemBase *layout) {
auto position = layout->position();
if (position < 0 || !isVisible()) {
return false;
@@ -930,12 +930,22 @@ void GifsListWidget::showPreview() {
}
void GifsListWidget::updateInlineItems() {
auto ms = crl::now();
if (_lastScrolled + 100 <= ms) {
update();
} else {
_updateInlineItems.callOnce(_lastScrolled + 100 - ms);
const auto now = crl::now();
const auto delay = std::max(
_lastScrolledAt + kMinAfterScrollDelay - now,
_lastUpdatedAt + kMinRepaintDelay - now);
if (delay <= 0) {
repaintItems(now);
} else if (!_updateInlineItems.isActive()
|| _updateInlineItems.remainingTime() > kMinRepaintDelay) {
_updateInlineItems.callOnce(std::max(delay, kMinRepaintDelay));
}
}
void GifsListWidget::repaintItems(crl::time now) {
_lastUpdatedAt = now ? now : crl::now();
update();
}
} // namespace ChatHelpers

View File

@@ -133,12 +133,14 @@ private:
void paintInlineItems(Painter &p, QRect clip);
void updateInlineItems();
void repaintItems(crl::time now = 0);
void showPreview();
MTP::Sender _api;
Section _section = Section::Gifs;
crl::time _lastScrolled = 0;
crl::time _lastScrolledAt = 0;
crl::time _lastUpdatedAt = 0;
base::Timer _updateInlineItems;
bool _inlineWithThumb = false;

View File

@@ -30,7 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_chat.h"
#include "base/qt_adapters.h"
#include "base/qt/qt_common_adapters.h"
#include <QtCore/QMimeData>
#include <QtCore/QStack>

View File

@@ -15,7 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "ui/widgets/popup_menu.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "history/history.h"
#include "history/history_unread_things.h"
#include "apiwrap.h"
#include "styles/style_menu_icons.h"
@@ -143,9 +146,11 @@ void SetupMenuAndShortcuts(
}, button->lifetime());
}
void SetupUnreadMentionsMenu(
void SetupReadAllMenu(
not_null<Ui::RpWidget*> button,
Fn<PeerData*()> currentPeer) {
Fn<PeerData*()> currentPeer,
const QString &text,
Fn<void(not_null<PeerData*>, Fn<void()>)> sendReadRequest) {
struct State {
base::unique_qptr<Ui::PopupMenu> menu;
base::flat_set<not_null<PeerData*>> sentForPeers;
@@ -159,19 +164,11 @@ void SetupUnreadMentionsMenu(
state->menu = base::make_unique_q<Ui::PopupMenu>(
button,
st::popupMenuWithIcons);
const auto text = tr::lng_context_mark_read_mentions_all(tr::now);
state->menu->addAction(text, [=] {
if (!state->sentForPeers.emplace(peer).second) {
return;
}
peer->session().api().request(MTPmessages_ReadMentions(
peer->input
)).done([=](const MTPmessages_AffectedHistory &result) {
state->sentForPeers.remove(peer);
peer->session().api().applyAffectedHistory(peer, result);
}).fail([=] {
state->sentForPeers.remove(peer);
}).send();
sendReadRequest(peer, [=] { state->sentForPeers.remove(peer); });
}, &st::menuIconMarkRead);
state->menu->popup(QCursor::pos());
};
@@ -183,7 +180,38 @@ void SetupUnreadMentionsMenu(
}
return base::EventFilterResult::Continue;
});
}
void SetupUnreadMentionsMenu(
not_null<Ui::RpWidget*> button,
Fn<PeerData*()> currentPeer) {
const auto text = tr::lng_context_mark_read_mentions_all(tr::now);
const auto sendRequest = [=](not_null<PeerData*> peer, Fn<void()> done) {
peer->session().api().request(MTPmessages_ReadMentions(
peer->input
)).done([=](const MTPmessages_AffectedHistory &result) {
done();
peer->session().api().applyAffectedHistory(peer, result);
peer->owner().history(peer)->unreadMentions().clear();
}).fail(done).send();
};
SetupReadAllMenu(button, currentPeer, text, sendRequest);
}
void SetupUnreadReactionsMenu(
not_null<Ui::RpWidget*> button,
Fn<PeerData*()> currentPeer) {
const auto text = tr::lng_context_mark_read_reactions_all(tr::now);
const auto sendRequest = [=](not_null<PeerData*> peer, Fn<void()> done) {
peer->session().api().request(MTPmessages_ReadReactions(
peer->input
)).done([=](const MTPmessages_AffectedHistory &result) {
done();
peer->session().api().applyAffectedHistory(peer, result);
peer->owner().history(peer)->unreadReactions().clear();
}).fail(done).send();
};
SetupReadAllMenu(button, currentPeer, text, sendRequest);
}
} // namespace SendMenu

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