Compare commits

..

182 Commits

Author SHA1 Message Date
John Preston
352768053d Beta version 3.1.6: Fix build on Linux. 2021-10-06 21:07:18 +04:00
John Preston
79b1cec4f3 Beta version 3.1.6: Remove -z,all from linker flags on Linux.
I hope it fixes #17037.
2021-10-06 20:37:39 +04:00
John Preston
8d09190439 Build ffmpeg without --disable-alsa/iconv on Linux. 2021-10-06 20:37:30 +04:00
John Preston
5cd0a3719e Beta version 3.1.6: Detach FastReply from MarkAsRead. 2021-10-06 19:24:30 +04:00
John Preston
8b7cd4a0c7 Beta version 3.1.6: Fix crash on old Windows 10 versions. 2021-10-06 17:36:30 +04:00
John Preston
937c2d3dce Beta version 3.1.6: Update patches revision. 2021-10-06 13:44:28 +04:00
23rd
1fa5d273cc Fixed scroll in Dialogs::Widget.
Regression was introduced in cb8f49aea0.
2021-10-06 13:40:26 +04:00
John Preston
24fa3dbf8f Beta version 3.1.6: Fix build on macOS. 2021-10-06 11:10:06 +04:00
John Preston
c9b782fd63 Beta version 3.1.6.
- Show small media previews in chats list.
- Show media album previews and caption text in chats list.
- Add "Quick Reply" and "Mark as Read" to native Windows notifications.
2021-10-06 11:07:38 +04:00
John Preston
e7cf560da0 Handle toast activations by COM activator. 2021-10-06 11:02:57 +04:00
John Preston
86e07518ad Fix clearing notifications from Action Center.
Regression was introduced in 997913be25.
2021-10-05 16:53:36 +04:00
John Preston
8c71d03959 Add reply from Windows native notifications. 2021-10-05 16:52:46 +04:00
John Preston
967e86f4ab Rewrite Windows native notifications using C++/WinRT. 2021-10-05 12:09:15 +04:00
John Preston
730412fefe Load albums of last chat messages. 2021-10-04 23:47:33 +04:00
John Preston
576883ddc8 Make mini preview radius 2px. 2021-10-04 23:47:33 +04:00
John Preston
992d636680 Generate album mini previews with up-to-three images. 2021-10-04 23:47:33 +04:00
John Preston
8cdd2f113f Add play icon to video mini previews. 2021-10-04 23:47:33 +04:00
John Preston
d5f935b73d Put mini-previews after sender name. 2021-10-04 23:47:33 +04:00
John Preston
84f561b251 Don't use MTP* in the image editor. 2021-10-04 23:47:33 +04:00
John Preston
21ac2b8f3a Don't use MTP* for reply markup data. 2021-10-04 23:47:30 +04:00
John Preston
1790828b01 Dump symbols from the binary instead of dSYM.
For some reason dump_syms from dSYM now fails with an error:

Telegram.app.dSYM/Contents/Resources/DWARF/Telegram:
the section '__text' in segment '__TEXT' claims its contents lie outside the segment's contents
2021-10-04 23:45:21 +04:00
John Preston
792b9090a7 Generate mini-previews for photos and files. 2021-10-04 23:45:21 +04:00
John Preston
8c21fad642 Move preview paint to Dialogs::Ui::MessageView. 2021-10-04 23:45:21 +04:00
John Preston
5136cc3c9c Rename Dialogs::Layout to Dialogs::Ui. 2021-10-04 23:45:21 +04:00
John Preston
b78b27f517 Move dialogs_layout to dialogs/ui/. 2021-10-04 23:45:21 +04:00
John Preston
85760ea92c Fix repeated attempt to transfer ownership.
Fixes #8570.
2021-10-04 23:45:21 +04:00
Ilya Fedin
c2212c719e Inform Qt about taskbar hider
This allows the feature to work without patching Qt
2021-10-04 23:23:50 +04:00
GitHub Action
fc8a0d0efd Update User-Agent for DNS to Chrome 94.0.4606.61. 2021-10-04 23:23:20 +04:00
23rd
c052c37621 Fixed build for non-Windows. 2021-09-30 21:04:43 +03:00
23rd
21f7cec781 Fixed build for macOS. 2021-09-30 21:21:07 +04:00
23rd
64af456d29 Fixed build for macOS. 2021-09-30 21:16:45 +04:00
John Preston
7751c4ac1f Port PQ factorization from TDLib. 2021-09-30 21:14:00 +04:00
23rd
ececdcb9c0 Removed Q_SLOTS from HistoryInner. 2021-09-30 18:45:38 +03:00
23rd
cb8f49aea0 Removed Q_OBJECT from ScrollArea. 2021-09-30 18:45:38 +03:00
23rd
e3ef7d6631 Removed MainWidget::highlightStartTime. 2021-09-30 18:45:38 +03:00
23rd
21aa1f49d7 Moved views increment scheduler from MainWidget to separate file. 2021-09-30 18:45:38 +03:00
John Preston
51e80170e2 Always clear passcode lock widget on reset.
Fixes #17016.
2021-09-30 19:37:42 +04:00
John Preston
b2526ab7f6 Make sure special MsgId-s are always outside ServerMaxMsgId range. 2021-09-30 19:31:03 +04:00
John Preston
e220447bdd Put the "N Seen" context menu item always first. 2021-09-30 15:36:14 +04:00
John Preston
ead695b101 Don't pass wide fake MsgId through MTPMessage. 2021-09-30 15:30:39 +04:00
John Preston
4ea72f8f89 Don't add "Change colors" to profile menu. 2021-09-30 15:30:29 +04:00
John Preston
4ef550da9b Hide webview while showing a box in payments. 2021-09-30 14:53:18 +04:00
John Preston
1e660fc2a2 Allocate 64 bits for message ids. 2021-09-30 13:49:37 +04:00
Ilya Fedin
6adf791b3b Update cmake_helpers 2021-09-29 09:04:23 +04:00
Ilya Fedin
d2a41a42e0 Move applicationDidFinishLaunching code to init
This should make the Qt patch catching the event unneeded
2021-09-29 09:04:23 +04:00
John Preston
315549b5f8 Beta version 3.1.5: Fix build on Linux. 2021-09-28 23:48:37 +04:00
John Preston
fd4a543bab Beta version 3.1.5: Fix theme change UI on Retina screens. 2021-09-28 22:27:41 +04:00
John Preston
d525e56053 Beta version 3.1.5: Fix build on Linux. 2021-09-28 22:08:28 +04:00
John Preston
dab5d1f994 Beta version 3.1.5.
- Choose one of 8 new preset themes for any individual private chat.
- Click on '...' menu > 'Change Colors' to pick a theme.
- Both chat participants will see the same theme
in that chat – on all their devices.
- Each new theme features colorful gradient message bubbles,
beautifully animated backgrounds and unique background patterns.
- All chat themes have day and night versions
and will follow your overall dark mode settings.
- Implement main window rounded corners on Windows 11.
- Fix audio capture from AirPods on macOS.
2021-09-28 22:00:51 +04:00
23rd
de3b52425c Removed unused HistoryInner::setFirstLoading. 2021-09-28 21:14:33 +04:00
John Preston
844fd58a97 Support Windows 11 rounded corners and themeable title bar. 2021-09-28 21:11:35 +04:00
John Preston
de2bad51d3 Scroll to currently selected theme. 2021-09-28 19:27:41 +04:00
John Preston
1424ea3540 Allow scrolling themes list. 2021-09-28 19:27:41 +04:00
John Preston
a8efd0ef3d Show chosen element in custom theme selector. 2021-09-28 19:27:41 +04:00
John Preston
1204e282d3 Fix attach icon in theme preview. 2021-09-28 19:27:41 +04:00
John Preston
6588242793 Prepare correct custom chat theme preview. 2021-09-28 19:27:41 +04:00
John Preston
b1ba9a42c6 Use Ui::GenerateBackgroundImage for preview in Settings. 2021-09-28 19:27:41 +04:00
John Preston
ab0d2bf9c6 Initial chat theme changing. 2021-09-28 19:27:41 +04:00
John Preston
80028e41f3 Bump OpenAL version in prepare script. 2021-09-28 19:25:39 +04:00
John Preston
2c581adc55 Add some hardening compiler / linker flags to dependencies. 2021-09-28 18:44:52 +04:00
John Preston
f0e8c1e325 Update lib_webview and docker patches revision. 2021-09-28 12:23:54 +04:00
John Preston
a2db9de4d7 Remove debug code. 2021-09-28 11:30:18 +04:00
John Preston
a228c62286 Fix "Nobody Viewed / Watched / Listened" seen state. 2021-09-27 18:51:50 +04:00
John Preston
37d940eca6 Beta version 3.1.4.
- Fix crash in network availability init.
- Fix assertion violation after a NaN-resulting std::round call.
2021-09-27 13:29:11 +04:00
John Preston
f7c24c54a1 Fix crash in failed network availability init. 2021-09-27 13:25:04 +04:00
John Preston
19ce1edc16 Use base::SafeRound instead of std::round.
Previous assertion violations because of NaN from std::round were
in video streaming, see commits 27d58ba07b, 8f5830d520.

Now the crashes happened in the ConvertScale() call from a background
thread when preparing an image from clipboard for sending to a chat.
2021-09-27 12:13:57 +04:00
John Preston
21b10cebe0 Beta version 3.1.3.
- Fix illegal instruction crash in opus encoder.
2021-09-27 10:28:29 +04:00
John Preston
50435f7783 Allow creating links with Ip addresses. 2021-09-27 10:28:29 +04:00
Ilya Fedin
1b789de4f4 Cherry-pick a opus fix for detecting CPU instructions on Windows 2021-09-27 08:50:51 +04:00
John Preston
a50310f0c1 Beta version 3.1.2 (Windows only).
- Control video in fullscreen mode using arrows and numbers.
- Open locations in browser if default Bing Maps is not installed.
- Reconnect without timeout when network availability changes.
- Crash fixes.
2021-09-26 17:24:09 +04:00
John Preston
eb02a7861a Add modern C++/WinRT headers path to Telegram project. 2021-09-26 17:20:22 +04:00
John Preston
8759ca4577 Rewrite bingmaps check to C++/WinRT. 2021-09-26 17:17:57 +04:00
Hermesis
d5c6d9a231 Open map in browser if default "bingmaps:" handler is not found 2021-09-26 17:16:38 +04:00
John Preston
63f179e93e Add Windows network reachability backend. 2021-09-26 14:31:54 +04:00
Hermesis
cfcc1b1ce7 Control video in fullscreen mode using arrows and numbers 2021-09-26 14:07:02 +04:00
John Preston
da1945d0ca Add some more information for an assertion violation. 2021-09-25 22:49:51 +04:00
John Preston
12252ef1aa One more attempt of OpenGL context loss crash fix. 2021-09-25 22:39:49 +04:00
John Preston
1eef94e8d9 Fix possible crash in delayed typing updates. 2021-09-25 22:39:11 +04:00
Ilya Fedin
0984e631fa Use opus built with cmake on Windows 2021-09-25 15:35:32 +04:00
John Preston
ec064a904d Version 3.1.1.
- Crash fixes.
2021-09-24 08:47:02 +04:00
John Preston
b47692e920 Update patches revision. 2021-09-24 08:46:52 +04:00
John Preston
132f127f3f Add some assertions to debug a crash. 2021-09-24 08:44:22 +04:00
John Preston
5c44b851fe One more attempt to fix crashes on context loss. 2021-09-23 17:39:39 +04:00
John Preston
2f5bed2899 Skip "contact joined" toast if disabled in Settings.
The chats still appear (they are server-side), but skip the toast.
2021-09-23 17:37:29 +04:00
John Preston
cf76933352 Fix saving tray "Toggle notifications" on relaunch.
Fixes https://bugs.telegram.org/c/9509
2021-09-22 14:03:41 +04:00
John Preston
eaa4c5e5b1 Fix caching by making a fake dir ThirdParty/gyp. 2021-09-22 12:42:59 +04:00
John Preston
a4b5b6e370 Fix crash in lottie destructor.
Fixes #16985.
2021-09-21 21:29:59 +04:00
John Preston
c1be1ca4ae Add ./build/prepare/linux.sh which builds docker. 2021-09-21 18:11:24 +04:00
Ilya Fedin
b2df781b76 Fix icon for Quit Telegram taskbar item in snap 2021-09-20 15:09:42 +03:00
Ilya Fedin
38815c1ca8 Backport fonts hook from snapcraft extensions 2021-09-20 11:40:03 +03:00
John Preston
2ec92f541c Version 3.1: Fix building Qt for Release. 2021-09-19 18:42:29 +03:00
John Preston
7ce8b42216 Version 3.1.
- Some animated emoji now have extra effects.
- Send 🎆 🎉, 🎈, :like:, 💩 or ❤️
to any private chat, then click on the animated emoji
to launch the effect.
- If your chat partner also has the chat open,
you will both see the effects.
- See the "Watching" status
when your chat partner is enjoying emoji effects with you.
- More interactive emoji coming soon.
- Right click one of your outgoing messages in small groups
to see who recently viewed it.
- To protect privacy, read receipts are only stored for 7 days
after the message was sent.
- Record video and audio from live broadcasts in your group or channel.
- Admins can start recording from the '...' menu.
- Choose between recording in portrait or landscape orientation.
- Finished recordings are sent to the admin's Saved Messages
and can be easily shared.
2021-09-19 18:21:31 +03:00
John Preston
17511749de Hold only last 5 custom themes in memory. 2021-09-19 18:15:46 +03:00
John Preston
4f6c7657bf Fix comments root pinned view disappearing. 2021-09-19 17:48:00 +03:00
John Preston
54085c70a4 Select light / dark custom theme based on dialogsBg value. 2021-09-19 17:39:29 +03:00
John Preston
e6c4b96c54 Just close SendFilesBox on last item remove.
Fixes https://bugs.telegram.org/c/2298
2021-09-19 17:36:31 +03:00
John Preston
b75221737a Make history-down button scale better. 2021-09-19 17:29:42 +03:00
John Preston
c336d725ea Fix media controls hiding workaround. 2021-09-19 16:55:55 +03:00
John Preston
d0fcc40d25 Don't play interactions in an inactive window. 2021-09-19 15:12:37 +03:00
John Preston
422bfd973b Don't play interactions without large emoji. 2021-09-19 15:12:20 +03:00
John Preston
d4db679ce8 Base custom chat themes on a separate light theme. 2021-09-19 14:43:09 +03:00
John Preston
2c7d8858c0 Base custom chat themes on a separate dark theme. 2021-09-19 14:43:09 +03:00
John Preston
155bbed3f4 Show correct video recording status. 2021-09-19 14:42:56 +03:00
John Preston
b1517c68fb Colorize outgoing selected state in light custom themes. 2021-09-19 14:42:51 +03:00
John Preston
d206ba7e1d Always underline links if color is the same as text. 2021-09-19 14:42:46 +03:00
John Preston
af100c2d13 Fix poll answer check in custom chat themes. 2021-09-19 14:40:43 +03:00
John Preston
1f25777929 Update watching emoji interaction phrase. 2021-09-19 14:40:43 +03:00
John Preston
a566405598 Show correct emoticon in interaction-seen status. 2021-09-19 14:40:43 +03:00
John Preston
b02967a44e Update tg_angle in prepare script. 2021-09-19 14:40:43 +03:00
John Preston
e0135e509d Allow exporting test chat themes. 2021-09-19 14:40:43 +03:00
John Preston
8274fddcbc Revert build-in Tinted theme.
Fixes #16969.
2021-09-19 14:40:43 +03:00
John Preston
82c45871c7 Fix build with Xcode. 2021-09-19 14:40:43 +03:00
23rd
2164caaab7 Fixed formatting of comments count. 2021-09-19 14:40:07 +03:00
Ilya Fedin
f4b162cbaf Update submodules 2021-09-19 13:22:38 +03:00
Ilya Fedin
4bc4584868 Build glibmm with LTO
In combination with https://github.com/desktop-app/cmake_helpers/pull/126 gets rid of unused glib symbols
2021-09-19 13:22:38 +03:00
John Preston
890a126423 Use shared provider for interaction animations. 2021-09-18 20:29:56 +03:00
John Preston
42cc24e167 Cache interactions in four cache keys. 2021-09-17 19:23:52 +03:00
John Preston
26b9146c32 Beta version 3.0.5: Try fixing messages skipping. 2021-09-17 14:27:34 +03:00
John Preston
ab6f5ae2ac Optimize bubble gradient background painting. 2021-09-17 13:22:33 +03:00
John Preston
559d4cf4da Update theme preview. 2021-09-17 13:22:33 +03:00
23rd
449f2d2f94 Migrated dependencies in Github CI for macOS. 2021-09-17 13:12:07 +03:00
John Preston
038f19d055 Beta version 3.0.5: Fix build on Linux. 2021-09-17 11:45:05 +03:00
John Preston
10d405aef4 Beta version 3.0.5: Add in-app changelog. 2021-09-17 10:57:42 +03:00
John Preston
efd4cceb19 Beta version 3.0.5.
- Add support for Emoji 13.1.
2021-09-17 10:56:16 +03:00
John Preston
822a3b69b5 Update libtgvoip. 2021-09-17 10:27:10 +03:00
John Preston
a632798383 Preserve alpha value in colorizer. 2021-09-17 10:25:37 +03:00
John Preston
cca08e3946 Fix crash in emoji interactions. 2021-09-17 10:25:36 +03:00
John Preston
4d267327b8 Update built-in Tinted dark theme. 2021-09-17 10:25:36 +03:00
John Preston
06798adce4 Accept skin colors and heart colors. 2021-09-17 10:25:36 +03:00
John Preston
34c0d97c54 Show emoji interaction seen status. 2021-09-17 10:25:36 +03:00
John Preston
4b7f594b0e Send emoji interaction seen requests. 2021-09-17 10:25:36 +03:00
John Preston
cfb43081c7 Play incoming interactions. 2021-09-17 10:25:36 +03:00
John Preston
703ea9aacd Apply scale keeping window center in place. 2021-09-17 10:25:36 +03:00
John Preston
de9b21e436 Correctly render interactions on inbox messages. 2021-09-17 10:25:36 +03:00
John Preston
73c0ea4b7d Don't mark as read when forwarding to scheduled. 2021-09-17 10:25:36 +03:00
John Preston
3e681e5449 Always force window frame inside screen geometry.
I hope this fixes #16934.
2021-09-17 10:25:36 +03:00
John Preston
bc2f96251f Limit amount of playing interactions. 2021-09-17 10:25:36 +03:00
John Preston
15f83892a1 Start emoji interactions playback. 2021-09-17 10:25:36 +03:00
John Preston
b6fafdd8f7 Limit emoji size to half of sticker size. 2021-09-17 10:25:36 +03:00
John Preston
139b9723d7 Accumulate and send emoji interactions. 2021-09-17 10:25:36 +03:00
John Preston
d152782115 Load and reload interaction stickers. 2021-09-17 10:25:36 +03:00
John Preston
35356a1736 Update API scheme. 2021-09-17 10:25:35 +03:00
Gleb Smirnoff
ef27670954 Fix compilation in presence of libzip installed.
When both minizip [1] and libzip [2] are present in the build
environment we have two <zip.h> includes. We are interested in
the minizip. Unfortunately, libzip usually installs its zip.h
straight into ${PREFIX}/include where lots of other headers reside.
We pick up ${PREFIX}/include into the include path with the
desktop-app::lib_base target, which is also dependency of
of tdesktop::td_* targets. To fix compilation in presence of
conflicting zip.h we need to put minizip's include directory
before ${PREFIX}/include, thus record its dependency before
all other dependencies that can bring desktop-app::lib_base.

[1] http://www.winimage.com/zLibDll/minizip.html
[2] https://libzip.org
2021-09-17 09:47:48 +03:00
Gleb Smirnoff
8cf9dc3319 Pull up to recent cmake_helpers and tg_owt that bring FreeBSD fixes. 2021-09-15 23:24:39 +03:00
Gleb Smirnoff
59f2f750b4 On FreeBSD the system malloc is jemalloc and non-portable
extensions are enabled including malloc_np.h.
2021-09-15 23:24:39 +03:00
John Preston
8069fdd873 Use base/random.h instead of openssl::RandomValue. 2021-09-15 13:42:22 +03:00
John Preston
52721847f4 Fix outgoing voice message unread mark. 2021-09-15 13:42:22 +03:00
John Preston
e492bbb883 Fix delayed pattern-with-negative-intensity appearance. 2021-09-14 16:36:30 +03:00
John Preston
7f20cc7b44 Update emoji to 13.1. 2021-09-14 15:14:25 +03:00
John Preston
7fbce765c9 Apply uniform-color palette to webpage preview titles. 2021-09-14 15:14:12 +03:00
John Preston
f771ad8cb1 Beta version 3.0.4: Fix build for Windows. 2021-09-13 20:13:15 +03:00
John Preston
6a53fc7edc Beta version 3.0.4: Add some theme loading logging. 2021-09-13 20:09:55 +03:00
John Preston
889c3293e7 Fix PathShiftGradient colors with custom palettes. 2021-09-13 20:09:23 +03:00
23rd
fa4b7145f5 Added extra space to choosing sticker animation in left position. 2021-09-13 18:13:50 +03:00
23rd
a5be9d78d8 Fixed animation for grouped userpics in who read context menu item. 2021-09-13 18:13:50 +03:00
John Preston
21c562fcb7 Make "set_version.py" working with Python 2.7 as well. 2021-09-13 18:03:43 +03:00
John Preston
626c062bf0 Beta version 3.0.4.
- Fix a crash when joining video chat or live broadcast.
- Add a "Close to Taskbar" option when tray icon is disabled
(Windows and Linux).
2021-09-13 18:01:39 +03:00
Ilya Fedin
e92ae40ecb Implement close to taskbar option 2021-09-13 17:46:53 +03:00
John Preston
ce256161f1 Couple of crash fixes. 2021-09-13 17:39:17 +03:00
John Preston
3bf9a1c70b Build minidump_stackwalk as part of macOS prepare. 2021-09-13 16:59:10 +03:00
John Preston
3202a5f081 Fix prepare getch for macOS. 2021-09-13 16:50:44 +03:00
John Preston
e99f650eaa Change default autoupdate URL. 2021-09-13 16:33:31 +03:00
John Preston
c6097d3d11 Accept "livestream" and "videochat" link params. 2021-09-13 16:19:59 +03:00
John Preston
58b5b3deec Update main built-in palette values. 2021-09-13 15:40:02 +03:00
John Preston
5a63428093 Improve messages in preparep.py. 2021-09-13 15:39:44 +03:00
John Preston
0c42bca111 Ask before rebuilding in prepare.py. 2021-09-13 15:01:52 +03:00
John Preston
13bf089672 Update patches revision. 2021-09-13 14:21:56 +03:00
John Preston
f73264025d Fix a crash after joining broadcasts. 2021-09-13 14:21:01 +03:00
Ilya Fedin
0a6fb696a3 Build Qt without egl-extension-platform-wayland 2021-09-13 10:51:35 +03:00
John Preston
bc2e6c4fd1 Accept uppercase hex values in theme testing links. 2021-09-12 23:18:37 +03:00
John Preston
d822f8e9ff Improve colorizing more. 2021-09-12 23:16:23 +03:00
John Preston
86362875dd Beta version 3.0.3.
- Try fixing crashes in allocator on Linux.
2021-09-12 01:53:04 +03:00
John Preston
0a4a96d4cd Fix action build on Linux. 2021-09-12 01:39:28 +03:00
John Preston
1c33eee80a Update hime to 0.9.11. 2021-09-12 00:33:35 +03:00
Ilya Fedin
4f5558d28c Move new dependencies to the beginning of Dockerfile, update patches
The reason was to not to do a full rebuild, but looks like the cache is already cleaned
2021-09-12 00:25:01 +03:00
Ilya Fedin
3fbd68cff9 Don't link glib with DESKTOP_APP_DISABLE_DBUS_INTEGRATION 2021-09-12 00:12:13 +03:00
Ilya Fedin
ee8c6f68d7 Use clang to build jemalloc since it crashes with gcc 2021-09-12 00:11:47 +03:00
John Preston
8d4174afb5 Fix macOS build in GitHub Actions. 2021-09-11 20:13:18 +03:00
John Preston
9150cc77f9 Improve colorizing of custom themes. 2021-09-11 12:26:35 +03:00
John Preston
8d31769846 Fix build with Xcode. 2021-09-11 00:02:15 +03:00
John Preston
13c00949ed Add a simple way of testing color themes. 2021-09-10 22:29:30 +03:00
John Preston
c4d822ba02 Fix action build on Windows. 2021-09-10 16:19:07 +03:00
326 changed files with 7554 additions and 2968 deletions

View File

@@ -53,7 +53,7 @@ jobs:
defaults:
run:
shell: scl enable devtoolset-9 -- bash --noprofile --norc -eo pipefail {0}
shell: scl enable llvm-toolset-7.0 -- scl enable devtoolset-9 -- bash --noprofile --norc -eo pipefail {0}
strategy:
matrix:

View File

@@ -13,7 +13,6 @@ on:
- '!.github/workflows/mac.yml'
- 'lib/xdg/**'
- 'snap/**'
- 'Telegram/build/**'
- 'Telegram/Resources/uwp/**'
- 'Telegram/Resources/winrc/**'
- 'Telegram/SourceFiles/platform/win/**'
@@ -31,7 +30,6 @@ on:
- '!.github/workflows/mac.yml'
- 'lib/xdg/**'
- 'snap/**'
- 'Telegram/build/**'
- 'Telegram/Resources/uwp/**'
- 'Telegram/Resources/winrc/**'
- 'Telegram/SourceFiles/platform/win/**'
@@ -49,19 +47,8 @@ jobs:
defines:
- ""
env:
MIN_MAC: "-mmacosx-version-min=10.12"
UNGUARDED: "-Werror=unguarded-availability-new"
GIT: "https://github.com"
MACOSX_DEPLOYMENT_TARGET: "10.12"
XZ: "xz-5.2.5"
QT: "5_15_2"
OPENSSL_VER: "1_1_1"
LIBICONV_VER: "libiconv-1.16"
UPLOAD_ARTIFACT: "false"
ONLY_CACHE: "false"
MANUAL_CACHING: "2"
DOC_PATH: "docs/building-mac.md"
AUTO_CACHING: "1"
steps:
- name: Get repository name.
@@ -84,433 +71,23 @@ jobs:
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
xcodebuild -version > CACHE_KEY.txt
echo $MIN_MAC >> CACHE_KEY.txt
echo $MANUAL_CACHING >> CACHE_KEY.txt
echo "$GITHUB_WORKSPACE" >> CACHE_KEY.txt
if [ "$AUTO_CACHING" == "1" ]; then
thisFile=$REPO_NAME/.github/workflows/mac.yml
echo `md5 -q $thisFile` >> CACHE_KEY.txt
fi
echo "CACHE_KEY=`md5 -q CACHE_KEY.txt`" >> $GITHUB_ENV
echo "$PWD/Libraries/depot_tools" >> $GITHUB_PATH
mkdir -p Libraries
cd Libraries
echo "LibrariesPath=`pwd`" >> $GITHUB_ENV
echo "PREFIX=`pwd`/local" >> $GITHUB_ENV
echo "QT_PREFIX=`pwd`/local/Qt-5.15.2" >> $GITHUB_ENV
curl -o tg_owt-version.json https://api.github.com/repos/desktop-app/tg_owt/git/refs/heads/master
- name: Patches.
run: |
echo "Find necessary commit from doc."
checkoutCommit=$(grep -A 1 "cd patches" $REPO_NAME/$DOC_PATH | sed -n 2p)
cd $LibrariesPath
git clone $GIT/desktop-app/patches.git
cd Patches
eval $checkoutCommit
- name: XZ.
run: |
cd $LibrariesPath
wget https://tukaani.org/xz/$XZ.tar.gz
tar -xvzf $XZ.tar.gz
cd $XZ
CFLAGS="$MIN_MAC" LDFLAGS="$MIN_MAC" ./configure --prefix=$PREFIX
make -j$(nproc)
make install
- name: Zlib.
run: |
cd $LibrariesPath
git clone $GIT/desktop-app/zlib.git
cd zlib
CFLAGS="$MIN_MAC $UNGUARDED" LDFLAGS="$MIN_MAC" ./configure --prefix=$PREFIX
make -j$(nproc)
make install
- name: MozJPEG.
run: |
cd $LibrariesPath
git clone -b v4.0.1-rc2 $GIT/mozilla/mozjpeg.git
cd mozjpeg
cmake -B build . \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$PREFIX \
-DWITH_JPEG8=ON \
-DPNG_SUPPORTED=OFF
cmake --build build -j$(nproc)
cmake --install build
- name: OpenSSL cache.
id: cache-openssl
- name: ThirdParty cache.
id: cache-third-party
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/openssl_${{ env.OPENSSL_VER }}
key: ${{ runner.OS }}-${{ env.OPENSSL_VER }}-${{ env.CACHE_KEY }}
- name: OpenSSL.
if: steps.cache-openssl.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
path: ThirdParty
key: ${{ runner.OS }}-third-party
git clone $GIT/openssl/openssl openssl
cd openssl
git checkout OpenSSL_"$OPENSSL_VER"-stable
./Configure \
--prefix=$PREFIX \
no-tests \
darwin64-x86_64-cc \
-static \
$MIN_MAC
make build_libs -j$(nproc)
SSL_DIR=$LibrariesPath/openssl_$OPENSSL_VER
mkdir -p $SSL_DIR/include
copyLib() {
cp $1.a $SSL_DIR/$1.a
}
copyLib libssl
copyLib libcrypto
cp -R include/. $SSL_DIR/include/
- name: Opus cache.
id: cache-opus
- name: Libraries cache.
id: cache-libs
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/opus-cache
key: ${{ runner.OS }}-opus-${{ env.CACHE_KEY }}
- name: Opus.
if: steps.cache-opus.outputs.cache-hit != 'true'
path: Libraries
key: ${{ runner.OS }}-libs
- name: Libraries.
run: |
cd $LibrariesPath
git clone $GIT/xiph/opus
cd opus
git checkout v1.3
./autogen.sh
CFLAGS="$MIN_MAC $UNGUARDED" CPPFLAGS="$MIN_MAC $UNGUARDED" LDFLAGS="$MIN_MAC" ./configure --prefix=$PREFIX
make -j$(nproc)
make DESTDIR="$LibrariesPath/opus-cache" install
- name: Opus install.
run: |
cd $LibrariesPath
cp -R opus-cache/. /
- name: Rnnoise.
run: |
cd $LibrariesPath
git clone $GIT/desktop-app/rnnoise.git
mkdir -p rnnoise/out/Debug
cd rnnoise/out/Debug
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ../..
ninja
- name: Libiconv cache.
id: cache-libiconv
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/libiconv-cache
key: ${{ runner.OS }}-${{ env.LIBICONV_VER }}-${{ env.CACHE_KEY }}
- name: Libiconv.
if: steps.cache-libiconv.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
wget https://ftp.gnu.org/pub/gnu/libiconv/"$LIBICONV_VER".tar.gz
tar -xvzf "$LIBICONV_VER".tar.gz
cd $LIBICONV_VER
CFLAGS="$MIN_MAC $UNGUARDED" CPPFLAGS="$MIN_MAC $UNGUARDED" LDFLAGS="$MIN_MAC" ./configure --enable-static --prefix=$PREFIX
make -j$(nproc)
make DESTDIR="$LibrariesPath/libiconv-cache" install
- name: Libiconv install.
run: |
cd $LibrariesPath
cp -R libiconv-cache/. /
- name: FFmpeg cache.
id: cache-ffmpeg
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/ffmpeg-cache
key: ${{ runner.OS }}-ffmpeg-${{ env.CACHE_KEY }}
- name: FFmpeg.
if: steps.cache-ffmpeg.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
git clone $GIT/FFmpeg/FFmpeg.git ffmpeg
cd ffmpeg
git checkout release/4.4
CFLAGS=`freetype-config --cflags`
LDFLAGS=`freetype-config --libs`
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig:/usr/X11/lib/pkgconfig
./configure --prefix=$LibrariesPath/ffmpeg-cache \
--extra-cflags="$MIN_MAC $UNGUARDED" \
--extra-cxxflags="$MIN_MAC $UNGUARDED" \
--extra-ldflags="$MIN_MAC" \
--x86asmexe=`pwd`/macos_yasm_wrap.sh \
--enable-protocol=file \
--enable-libopus \
--disable-programs \
--disable-doc \
--disable-network \
--disable-everything \
--enable-hwaccel=h264_videotoolbox \
--enable-hwaccel=hevc_videotoolbox \
--enable-hwaccel=mpeg1_videotoolbox \
--enable-hwaccel=mpeg2_videotoolbox \
--enable-hwaccel=mpeg4_videotoolbox \
--enable-decoder=aac \
--enable-decoder=aac_at \
--enable-decoder=aac_fixed \
--enable-decoder=aac_latm \
--enable-decoder=aasc \
--enable-decoder=alac \
--enable-decoder=alac_at \
--enable-decoder=flac \
--enable-decoder=gif \
--enable-decoder=h264 \
--enable-decoder=hevc \
--enable-decoder=mp1 \
--enable-decoder=mp1float \
--enable-decoder=mp2 \
--enable-decoder=mp2float \
--enable-decoder=mp3 \
--enable-decoder=mp3adu \
--enable-decoder=mp3adufloat \
--enable-decoder=mp3float \
--enable-decoder=mp3on4 \
--enable-decoder=mp3on4float \
--enable-decoder=mpeg4 \
--enable-decoder=msmpeg4v2 \
--enable-decoder=msmpeg4v3 \
--enable-decoder=opus \
--enable-decoder=pcm_alaw \
--enable-decoder=pcm_alaw_at \
--enable-decoder=pcm_f32be \
--enable-decoder=pcm_f32le \
--enable-decoder=pcm_f64be \
--enable-decoder=pcm_f64le \
--enable-decoder=pcm_lxf \
--enable-decoder=pcm_mulaw \
--enable-decoder=pcm_mulaw_at \
--enable-decoder=pcm_s16be \
--enable-decoder=pcm_s16be_planar \
--enable-decoder=pcm_s16le \
--enable-decoder=pcm_s16le_planar \
--enable-decoder=pcm_s24be \
--enable-decoder=pcm_s24daud \
--enable-decoder=pcm_s24le \
--enable-decoder=pcm_s24le_planar \
--enable-decoder=pcm_s32be \
--enable-decoder=pcm_s32le \
--enable-decoder=pcm_s32le_planar \
--enable-decoder=pcm_s64be \
--enable-decoder=pcm_s64le \
--enable-decoder=pcm_s8 \
--enable-decoder=pcm_s8_planar \
--enable-decoder=pcm_u16be \
--enable-decoder=pcm_u16le \
--enable-decoder=pcm_u24be \
--enable-decoder=pcm_u24le \
--enable-decoder=pcm_u32be \
--enable-decoder=pcm_u32le \
--enable-decoder=pcm_u8 \
--enable-decoder=vorbis \
--enable-decoder=wavpack \
--enable-decoder=wmalossless \
--enable-decoder=wmapro \
--enable-decoder=wmav1 \
--enable-decoder=wmav2 \
--enable-decoder=wmavoice \
--enable-encoder=libopus \
--enable-parser=aac \
--enable-parser=aac_latm \
--enable-parser=flac \
--enable-parser=h264 \
--enable-parser=hevc \
--enable-parser=mpeg4video \
--enable-parser=mpegaudio \
--enable-parser=opus \
--enable-parser=vorbis \
--enable-demuxer=aac \
--enable-demuxer=flac \
--enable-demuxer=gif \
--enable-demuxer=h264 \
--enable-demuxer=hevc \
--enable-demuxer=m4v \
--enable-demuxer=mov \
--enable-demuxer=mp3 \
--enable-demuxer=ogg \
--enable-demuxer=wav \
--enable-muxer=ogg \
--enable-muxer=opus
make -j$(nproc)
make install
- name: FFmpeg install.
run: |
cd $LibrariesPath
# List of files from cmake/external/ffmpeg/CMakeLists.txt.
copyLib() {
mkdir -p ffmpeg/$1
\cp -fR ffmpeg-cache/lib/$1.a ffmpeg/$1/$1.a
}
copyLib libavformat
copyLib libavcodec
copyLib libswresample
copyLib libswscale
copyLib libavutil
cp -R ffmpeg-cache/. $PREFIX
cp -R ffmpeg-cache/include/. ffmpeg/
- name: OpenAL Soft.
run: |
cd $LibrariesPath
git clone --branch capture_with_webrtc $GIT/telegramdesktop/openal-soft.git
cd openal-soft/build
CFLAGS="$UNGUARDED" CPPFLAGS="$UNGUARDED" cmake \
-D CMAKE_INSTALL_PREFIX:PATH=$PREFIX \
-D ALSOFT_EXAMPLES=OFF \
-D LIBTYPE:STRING=STATIC \
-D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET ..
make -j$(nproc)
make install
- name: Crashpad cache.
id: cache-crashpad
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/crashpad
key: ${{ runner.OS }}-crashpad-${{ env.CACHE_KEY }}-${{ hashFiles('**/crashpad.diff') }}-${{ hashFiles('**/mini_chromium.diff') }}
- name: Crashpad.
if: steps.cache-crashpad.outputs.cache-hit != 'true'
run: |
cd Libraries
echo Install GYP for Crashpad.
git clone https://chromium.googlesource.com/external/gyp
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
cd gyp
git checkout 9f2a7bb1
git apply $LibrariesPath/patches/gyp.diff
./setup.py build
sudo ./setup.py install
cd $LibrariesPath
git clone https://chromium.googlesource.com/crashpad/crashpad.git
cd crashpad
git checkout feb3aa3923
git apply ../patches/crashpad.diff
cd third_party/mini_chromium
git clone https://chromium.googlesource.com/chromium/mini_chromium
cd mini_chromium
git checkout 7c5b0c1ab4
git apply ../../../../patches/mini_chromium.diff
cd ../../gtest
git clone https://chromium.googlesource.com/external/github.com/google/googletest gtest
cd gtest
git checkout d62d6c6556
cd ../../..
build/gyp_crashpad.py -Dmac_deployment_target=10.10
ninja -C out/Debug
- name: Qt 5.15.2 cache.
id: cache-qt
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/qt-cache
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_5_15_2/*') }}
- name: Use cached Qt 5.15.2.
if: steps.cache-qt.outputs.cache-hit == 'true'
run: |
cd $LibrariesPath
mv qt-cache Qt-5.15.2
mkdir -p $QT_PREFIX
mv -f Qt-5.15.2 "$(dirname "$QT_PREFIX")"/
- name: Qt 5.15.2 build.
if: steps.cache-qt.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
git clone git://code.qt.io/qt/qt5.git qt_$QT
cd qt_$QT
perl init-repository --module-subset=qtbase,qtimageformats,qtsvg
git checkout v5.15.2
git submodule update qtbase
git submodule update qtimageformats
git submodule update qtsvg
cd qtbase
find ../../patches/qtbase_$QT -type f -print0 | sort -z | xargs -0 git apply
cd ..
./configure \
-prefix "$QT_PREFIX" \
-debug \
-force-debug-info \
-opensource \
-confirm-license \
-static \
-opengl desktop \
-no-openssl \
-securetransport \
-nomake examples \
-nomake tests \
-platform macx-clang \
-I "$PREFIX/include" \
LIBJPEG_LIBS="$PREFIX/lib/libjpeg.a" \
ZLIB_LIBS="$PREFIX/lib/libz.a"
make -j$(nproc)
make install
make clean
cp -r $QT_PREFIX $LibrariesPath/qt-cache
- name: WebRTC cache.
id: cache-webrtc
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/tg_owt
key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}-${{ hashFiles('**/tg_owt-version.json') }}
- name: WebRTC.
if: steps.cache-webrtc.outputs.cache-hit != 'true'
run: |
cd $LibrariesPath
git clone --recursive $GIT/desktop-app/tg_owt.git
mkdir -p tg_owt/out/Debug
cd tg_owt/out/Debug
cmake -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DTG_OWT_SPECIAL_TARGET=mac \
-DTG_OWT_BUILD_AUDIO_BACKENDS=OFF \
-DTG_OWT_LIBJPEG_INCLUDE_PATH=$PREFIX/include \
-DTG_OWT_OPENSSL_INCLUDE_PATH=`pwd`/../../../openssl_$OPENSSL_VER/include \
-DTG_OWT_OPUS_INCLUDE_PATH=$PREFIX/include/opus \
-DTG_OWT_FFMPEG_INCLUDE_PATH=$PREFIX/include \
../..
ninja
# Cleanup.
cd $LibrariesPath/tg_owt
mv out/Debug/libtg_owt.a libtg_owt.a
rm -rf out
mkdir -p out/Debug
mv libtg_owt.a out/Debug/libtg_owt.a
./$REPO_NAME/Telegram/build/prepare/mac.sh skip-release silent
- name: Telegram Desktop build.
if: env.ONLY_CACHE == 'false'

View File

@@ -58,7 +58,7 @@ jobs:
UPLOAD_ARTIFACT: "false"
ONLY_CACHE: "false"
MANUAL_CACHING: "0"
DOC_PATH: "docs/building-win.md"
PREPARE_PATH: "Telegram/build/prepare/prepare.py"
AUTO_CACHING: "1"
defaults:
@@ -132,24 +132,12 @@ jobs:
working-directory: ${{ github.workspace }}
run: |
echo "Find necessary commit from doc."
checkoutCommit=$(grep -A 1 "cd patches" $REPO_NAME/$DOC_PATH | sed -n 2p)
checkoutCommit=$(grep -A 1 "cd patches" $REPO_NAME/$PREPARE_PATH | sed -n 2p)
cd $LibrariesPath
git clone $GIT/desktop-app/patches.git
cd Patches
cd patches
eval $checkoutCommit
- name: Find any version of Python 2.
shell: bash
run: |
echo "Find any version of Python 2."
p=`ls /c/hostedtoolcache/windows/python | grep "^2" | tail -1`
if [ -z "$p" ]; then
echo "Python 2 is not found."
exit 1
fi
echo "PY2=C:\\hostedtoolcache\\windows\\Python\\$p\\x64" >> $GITHUB_ENV
echo "Found $p."
- name: LZMA.
run: |
git clone %GIT%/telegramdesktop/lzma.git
@@ -161,28 +149,27 @@ jobs:
id: cache-openssl
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/openssl_${{ env.OPENSSL_VER }}
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 %GIT%/openssl/openssl.git openssl_%OPENSSL_VER%
cd openssl_%OPENSSL_VER%
git checkout OpenSSL_%OPENSSL_VER%-stable
git clone -b OpenSSL_%OPENSSL_VER%-stable %GIT%/openssl/openssl.git
cd openssl
perl Configure no-shared no-tests debug-VC-WIN32
nmake
mkdir out32.dbg
move libcrypto.lib out32.dbg
move libssl.lib out32.dbg
move ossl_static.pdb out32.dbg\ossl_static
mkdir out.dbg
move libcrypto.lib out.dbg
move libssl.lib out.dbg
move ossl_static.pdb out.dbg\ossl_static
nmake clean
move out32.dbg\ossl_static out32.dbg\ossl_static.pdb
move out.dbg\ossl_static out.dbg\ossl_static.pdb
perl Configure no-shared no-tests VC-WIN32
nmake
mkdir out32
move libcrypto.lib out32
move libssl.lib out32
move ossl_static.pdb out32
mkdir out
move libcrypto.lib out
move libssl.lib out
move ossl_static.pdb out
rmdir /S /Q test
rmdir /S /Q .git
@@ -239,16 +226,17 @@ jobs:
GYP_MSVS_VERSION: 2019
if: steps.cache-breakpad.outputs.cache-hit != 'true'
run: |
git clone %GIT%/telegramdesktop/gyp.git
git clone https://chromium.googlesource.com/external/gyp
cd gyp
SET PATH=%PY2%;%cd%;%PATH%
git checkout tdesktop
SET PATH=%cd%;%PATH%
git checkout d6c5dd51dc
git apply ../patches/gyp.diff
cd %LibrariesPath%
git clone %GIT%/google/breakpad
git clone https://chromium.googlesource.com/breakpad/breakpad
cd breakpad
git checkout a1dbcdcb43
git checkout bc8fb886
git apply ../patches/breakpad.diff
cd src
git clone %GIT%/google/googletest testing
@@ -258,10 +246,9 @@ jobs:
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
call vcvars32.bat
msbuild -m dump_syms.vcxproj /property:Configuration=Release
call gyp dump_syms.gyp --format=ninja
cd ..\..\..
ninja -C out/Release dump_syms
- name: Opus cache.
id: cache-opus
@@ -272,12 +259,17 @@ jobs:
- name: Opus.
if: steps.cache-opus.outputs.cache-hit != 'true'
run: |
git clone %GIT%/telegramdesktop/opus.git
git clone -b v1.3.1 %GIT%/xiph/opus.git
cd opus
git checkout tdesktop
cd win32\VS2015
msbuild -m opus.sln /property:Configuration=Debug /property:Platform="Win32"
msbuild -m opus.sln /property:Configuration=Release /property:Platform="Win32"
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: |
@@ -352,7 +344,7 @@ jobs:
for /r %%i in (..\..\patches\qtbase_%QT%\*) do git apply %%i
cd ..
SET SSL=%LibrariesPath%\openssl_%OPENSSL_VER%
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
@@ -380,8 +372,8 @@ jobs:
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%\out32.dbg\libssl.lib %SSL%\out32.dbg\%SSL_LIBS%" ^
OPENSSL_LIBS_RELEASE="%SSL%\out32\libssl.lib %SSL%\out32\%SSL_LIBS%" ^
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" ^
@@ -418,7 +410,7 @@ jobs:
-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_%OPENSSL_VER%/include ^
-DTG_OWT_OPENSSL_INCLUDE_PATH=%cd%/../../../openssl/include ^
-DTG_OWT_OPUS_INCLUDE_PATH=%cd%/../../../opus/include ^
-DTG_OWT_FFMPEG_INCLUDE_PATH=%cd%/../../../ffmpeg ^
../..

View File

@@ -42,7 +42,12 @@ include(cmake/generate_appdata_changelog.cmake)
if (WIN32)
include(cmake/generate_midl.cmake)
generate_midl(Telegram ${src_loc}/platform/win/windows_quiethours.idl)
generate_midl(Telegram ${src_loc}
platform/win/windows_quiethours.idl
platform/win/windows_toastactivator.idl
)
nuget_add_winrt(Telegram)
endif()
set_target_properties(Telegram PROPERTIES AUTOMOC ON AUTORCC ON)
@@ -52,6 +57,14 @@ PRIVATE
tdesktop::lib_tgcalls_legacy
tdesktop::lib_tgcalls
tdesktop::lib_tgvoip
# Order in this list defines the order of include paths in command line.
# We need to place desktop-app::external_minizip this early to have its
# include paths (usually ${PREFIX}/include/minizip) before any depend that
# would add ${PREFIX}/include. This path may have a different <zip.h>,
# for example installed by libzip (https://libzip.org).
desktop-app::external_minizip
tdesktop::td_export
tdesktop::td_mtproto
tdesktop::td_lang
@@ -71,7 +84,6 @@ PRIVATE
desktop-app::external_lz4
desktop-app::external_rlottie
desktop-app::external_zlib
desktop-app::external_minizip
desktop-app::external_qt_static_plugins
desktop-app::external_qt
desktop-app::external_qr_code_generator
@@ -130,6 +142,8 @@ PRIVATE
api/api_updates.h
api/api_user_privacy.cpp
api/api_user_privacy.h
api/api_views.cpp
api/api_views.h
api/api_who_read.cpp
api/api_who_read.h
boxes/filters/edit_filter_box.cpp
@@ -278,6 +292,8 @@ PRIVATE
chat_helpers/bot_command.h
chat_helpers/bot_keyboard.cpp
chat_helpers/bot_keyboard.h
chat_helpers/emoji_interactions.cpp
chat_helpers/emoji_interactions.h
chat_helpers/emoji_keywords.cpp
chat_helpers/emoji_keywords.h
chat_helpers/emoji_list_widget.cpp
@@ -404,6 +420,7 @@ PRIVATE
data/data_media_types.h
data/data_messages.cpp
data/data_messages.h
data/data_msg_id.h
data/data_notify_settings.cpp
data/data_notify_settings.h
data/data_peer.cpp
@@ -456,8 +473,6 @@ PRIVATE
dialogs/dialogs_inner_widget.h
dialogs/dialogs_key.cpp
dialogs/dialogs_key.h
dialogs/dialogs_layout.cpp
dialogs/dialogs_layout.h
dialogs/dialogs_list.cpp
dialogs/dialogs_list.h
dialogs/dialogs_main_list.cpp
@@ -470,6 +485,10 @@ PRIVATE
dialogs/dialogs_search_from_controllers.h
dialogs/dialogs_widget.cpp
dialogs/dialogs_widget.h
dialogs/ui/dialogs_layout.cpp
dialogs/ui/dialogs_layout.h
dialogs/ui/dialogs_message_view.cpp
dialogs/ui/dialogs_message_view.h
editor/color_picker.cpp
editor/color_picker.h
editor/controllers/controllers.h
@@ -581,6 +600,8 @@ PRIVATE
history/view/history_view_cursor_state.h
history/view/history_view_element.cpp
history/view/history_view_element.h
history/view/history_view_emoji_interactions.cpp
history/view/history_view_emoji_interactions.h
history/view/history_view_empty_list_bubble.cpp
history/view/history_view_empty_list_bubble.h
history/view/history_view_group_call_tracker.cpp
@@ -618,6 +639,8 @@ PRIVATE
history/history_item.h
history/history_item_components.cpp
history/history_item_components.h
history/history_item_reply_markup.cpp
history/history_item_reply_markup.h
history/history_item_text.cpp
history/history_item_text.h
history/history_inner_widget.cpp
@@ -930,6 +953,8 @@ PRIVATE
platform/win/windows_dlls.h
platform/win/windows_event_filter.cpp
platform/win/windows_event_filter.h
platform/win/windows_toast_activator.cpp
platform/win/windows_toast_activator.h
platform/platform_audio.h
platform/platform_file_utilities.h
platform/platform_launcher.h
@@ -1027,6 +1052,8 @@ PRIVATE
ui/chat/attach/attach_item_single_file_preview.h
ui/chat/attach/attach_item_single_media_preview.cpp
ui/chat/attach/attach_item_single_media_preview.h
ui/chat/choose_theme_controller.cpp
ui/chat/choose_theme_controller.h
ui/effects/fireworks_animation.cpp
ui/effects/fireworks_animation.h
ui/effects/round_checkbox.cpp
@@ -1245,17 +1272,13 @@ elseif (APPLE)
endif()
endif()
else()
target_link_libraries(Telegram
PRIVATE
desktop-app::external_glibmm
desktop-app::external_glib
)
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
)
endif()
@@ -1359,12 +1382,14 @@ if (WIN32)
/DELAYLOAD:gdiplus.dll
/DELAYLOAD:version.dll
/DELAYLOAD:dwmapi.dll
/DELAYLOAD:uxtheme.dll
/DELAYLOAD:crypt32.dll
/DELAYLOAD:bcrypt.dll
/DELAYLOAD:imm32.dll
/DELAYLOAD:netapi32.dll
/DELAYLOAD:userenv.dll
/DELAYLOAD:wtsapi32.dll
/DELAYLOAD:propsys.dll
)
endif()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 594 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 946 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 651 B

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -357,6 +357,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_update_fail" = "Update check failed :(";
"lng_settings_workmode_tray" = "Show tray icon";
"lng_settings_workmode_window" = "Show taskbar icon";
"lng_settings_close_to_taskbar" = "Close to taskbar";
"lng_settings_native_frame" = "Use system window frame";
"lng_settings_auto_start" = "Launch Telegram when system starts";
"lng_settings_start_min" = "Launch minimized";
@@ -1617,6 +1618,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_user_action_upload_file" = "{user} is sending a file";
"lng_send_action_choose_sticker" = "choosing a sticker";
"lng_user_action_choose_sticker" = "{user} is choosing a sticker";
"lng_user_action_watching_animations" = "watching {emoji}";
"lng_unread_bar#one" = "{count} unread message";
"lng_unread_bar#other" = "{count} unread messages";
"lng_unread_bar_some" = "Unread messages";
@@ -2237,7 +2239,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_recording_start_checkbox" = "Also record video";
"lng_group_call_recording_start_audio_subtitle" = "This chat will be recorded into an audio file";
"lng_group_call_recording_start_video_subtitle" = "Choose video orientation";
"lng_group_call_is_recorded" = "Voice chat is being recorded.";
"lng_group_call_is_recorded" = "The audio stream is being recorded.";
"lng_group_call_is_recorded_video" = "The video stream is being recorded.";
"lng_group_call_is_recorded_channel" = "Live stream is being recorded.";
"lng_group_call_can_speak_here" = "You can now speak.";
"lng_group_call_can_speak" = "You can now speak in {chat}.";
@@ -2868,6 +2871,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_filters_remove_sure" = "This will remove the folder, your chats will not be deleted.";
"lng_filters_remove_yes" = "Remove";
"lng_chat_theme_change" = "Change colors";
"lng_chat_theme_none" = "No\nTheme";
"lng_chat_theme_apply" = "Apply Theme";
"lng_chat_theme_reset" = "Reset Theme";
"lng_chat_theme_dont" = "Do Not Set Theme";
"lng_chat_theme_title" = "Select theme";
"lng_chat_theme_cant_voice" = "Sorry, you can't change the chat theme while you're having an unsent voice message.";
"lng_photo_editor_menu_delete" = "Delete";
"lng_photo_editor_menu_flip" = "Flip";
"lng_photo_editor_menu_duplicate" = "Duplicate";

Binary file not shown.

View File

@@ -47,7 +47,7 @@
<file alias="art/bg_initial.jpg">../../art/bg_initial.jpg</file>
<file alias="art/logo_256.png">../../art/logo_256.png</file>
<file alias="art/logo_256_no_margin.png">../../art/logo_256_no_margin.png</file>
<file alias="art/sunrise.jpg">../../art/sunrise.jpg</file>
<file alias="art/themeimage.jpg">../../art/themeimage.jpg</file>
<file alias="art/dice_idle.tgs">../../art/dice_idle.tgs</file>
<file alias="art/dart_idle.tgs">../../art/dart_idle.tgs</file>
<file alias="art/bball_idle.tgs">../../art/bball_idle.tgs</file>
@@ -60,6 +60,8 @@
<file alias="day-blue.tdesktop-theme">../../day-blue.tdesktop-theme</file>
<file alias="night.tdesktop-theme">../../night.tdesktop-theme</file>
<file alias="night-green.tdesktop-theme">../../night-green.tdesktop-theme</file>
<file alias="day-custom-base.tdesktop-theme">../../day-custom-base.tdesktop-theme</file>
<file alias="night-custom-base.tdesktop-theme">../../night-custom-base.tdesktop-theme</file>
<file alias="icons/calls/hands.lottie">../../icons/calls/hands.lottie</file>
<file alias="icons/calls/voice.lottie">../../icons/calls/voice.lottie</file>
<file alias="recording/info_audio.svg">../../art/recording/recording_info_audio.svg</file>

View File

@@ -467,7 +467,7 @@ sendMessageUploadRoundAction#243e1c66 progress:int = SendMessageAction;
speakingInGroupCallAction#d92c2285 = SendMessageAction;
sendMessageHistoryImportAction#dbda9246 progress:int = SendMessageAction;
sendMessageChooseStickerAction#b05ac6b1 = SendMessageAction;
sendMessageEmojiInteraction#6a3233b6 emoticon:string interaction:DataJSON = SendMessageAction;
sendMessageEmojiInteraction#25972bcb emoticon:string msg_id:int interaction:DataJSON = SendMessageAction;
sendMessageEmojiInteractionSeen#b665902e emoticon:string = SendMessageAction;
contacts.found#b3134d9d my_results:Vector<Peer> results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found;
@@ -560,6 +560,7 @@ inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
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;
@@ -907,7 +908,6 @@ channelAdminLogEventActionExportedInviteRevoke#410a134e invite:ExportedChatInvit
channelAdminLogEventActionExportedInviteEdit#e90ebb59 prev_invite:ExportedChatInvite new_invite:ExportedChatInvite = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantVolume#3e7f6847 participant:GroupCallParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeTheme#fe69018d prev_value:string new_value:string = ChannelAdminLogEventAction;
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "api/api_cloud_password.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include "core/core_cloud_password.h"
#include "apiwrap.h"
@@ -27,7 +27,7 @@ void CloudPassword::reload() {
)).done([=](const MTPaccount_Password &result) {
_requestId = 0;
result.match([&](const MTPDaccount_password &data) {
openssl::AddRandomSeed(bytes::make_span(data.vsecure_random().v));
base::RandomAddSeed(bytes::make_span(data.vsecure_random().v));
if (_state) {
*_state = Core::ParseCloudPasswordState(data);
} else {

View File

@@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_sending.h"
#include "api/api_text_entities.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include "base/unixtime.h"
#include "data/data_document.h"
#include "data/data_photo.h"
@@ -76,7 +76,7 @@ void SendExistingMedia(
const auto newId = FullMsgId(
peerToChannel(peer->id),
session->data().nextLocalMessageId());
const auto randomId = openssl::RandomValue<uint64>();
const auto randomId = base::RandomValue<uint64>();
auto flags = NewMessageFlags(peer);
auto sendFlags = MTPmessages_SendMedia::Flags(0);
@@ -128,7 +128,7 @@ void SendExistingMedia(
messagePostAuthor,
media,
caption,
MTPReplyMarkup());
HistoryMessageMarkupData());
auto performRequest = [=](const auto &repeatRequest) -> void {
auto &histories = history->owner().histories();
@@ -248,7 +248,7 @@ bool SendDice(Api::MessageToSend &message) {
const auto newId = FullMsgId(
peerToChannel(peer->id),
session->data().nextLocalMessageId());
const auto randomId = openssl::RandomValue<uint64>();
const auto randomId = base::RandomValue<uint64>();
auto &histories = history->owner().histories();
auto flags = NewMessageFlags(peer);
@@ -288,7 +288,7 @@ bool SendDice(Api::MessageToSend &message) {
messagePostAuthor,
TextWithEntities(),
MTP_messageMediaDice(MTP_int(0), MTP_string(emoji)),
MTPReplyMarkup());
HistoryMessageMarkupData());
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
@@ -397,6 +397,11 @@ void SendConfirmedFile(
} else {
flags |= MessageFlag::LocalHistoryEntry;
}
if (file->type == SendMediaType::Audio) {
if (!peer->isChannel() || peer->isMegagroup()) {
flags |= MessageFlag::MediaIsUnread;
}
}
const auto messageFromId = anonymousPost ? 0 : session->userPeerId();
const auto messagePostAuthor = peer->isBroadcast()
@@ -466,7 +471,7 @@ void SendConfirmedFile(
messagePostAuthor,
caption,
media,
MTPReplyMarkup(),
HistoryMessageMarkupData(),
groupId);
}

View File

@@ -29,6 +29,7 @@ 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 "chat_helpers/emoji_interactions.h"
#include "lang/lang_cloud_manager.h"
#include "history/history.h"
#include "history/history_item.h"
@@ -984,36 +985,18 @@ void Updates::handleSendActionUpdate(
const auto from = (fromId == session().userPeerId())
? session().user().get()
: session().data().peerLoaded(fromId);
const auto isSpeakingInCall = (action.type()
== mtpc_speakingInGroupCallAction);
if (isSpeakingInCall) {
if (!peer->isChat() && !peer->isChannel()) {
return;
}
const auto call = peer->groupCall();
const auto now = crl::now();
if (call) {
call->applyActiveUpdate(
fromId,
Data::LastSpokeTimes{ .anything = now, .voice = now },
from);
} else {
const auto chat = peer->asChat();
const auto channel = peer->asChannel();
const auto active = chat
? (chat->flags() & ChatDataFlag::CallActive)
: (channel->flags() & ChannelDataFlag::CallActive);
if (active) {
_pendingSpeakingCallParticipants.emplace(
peer).first->second[fromId] = now;
if (peerIsUser(fromId)) {
session().api().requestFullPeer(peer);
}
}
}
if (action.type() == mtpc_speakingInGroupCallAction) {
handleSpeakingInCall(peer, fromId, from);
}
if (!from || !from->isUser() || from->isSelf()) {
return;
} else if (action.type() == mtpc_sendMessageEmojiInteraction) {
handleEmojiInteraction(peer, action.c_sendMessageEmojiInteraction());
return;
} else if (action.type() == mtpc_sendMessageEmojiInteractionSeen) {
const auto &data = action.c_sendMessageEmojiInteractionSeen();
handleEmojiInteraction(peer, qs(data.vemoticon()));
return;
}
const auto when = requestingDifference()
? 0
@@ -1026,6 +1009,76 @@ void Updates::handleSendActionUpdate(
when);
}
void Updates::handleEmojiInteraction(
not_null<PeerData*> peer,
const MTPDsendMessageEmojiInteraction &data) {
const auto json = data.vinteraction().match([&](
const MTPDdataJSON &data) {
return data.vdata().v;
});
handleEmojiInteraction(
peer,
data.vmsg_id().v,
qs(data.vemoticon()),
ChatHelpers::EmojiInteractions::Parse(json));
}
void Updates::handleSpeakingInCall(
not_null<PeerData*> peer,
PeerId participantPeerId,
PeerData *participantPeerLoaded) {
if (!peer->isChat() && !peer->isChannel()) {
return;
}
const auto call = peer->groupCall();
const auto now = crl::now();
if (call) {
call->applyActiveUpdate(
participantPeerId,
Data::LastSpokeTimes{ .anything = now, .voice = now },
participantPeerLoaded);
} else {
const auto chat = peer->asChat();
const auto channel = peer->asChannel();
const auto active = chat
? (chat->flags() & ChatDataFlag::CallActive)
: (channel->flags() & ChannelDataFlag::CallActive);
if (active) {
_pendingSpeakingCallParticipants.emplace(
peer).first->second[participantPeerId] = now;
if (peerIsUser(participantPeerId)) {
session().api().requestFullPeer(peer);
}
}
}
}
void Updates::handleEmojiInteraction(
not_null<PeerData*> peer,
MsgId messageId,
const QString &emoticon,
ChatHelpers::EmojiInteractionsBunch bunch) {
if (session().windows().empty()) {
return;
}
const auto window = session().windows().front();
window->emojiInteractions().startIncoming(
peer,
messageId,
emoticon,
std::move(bunch));
}
void Updates::handleEmojiInteraction(
not_null<PeerData*> peer,
const QString &emoticon) {
if (session().windows().empty()) {
return;
}
const auto window = session().windows().front();
window->emojiInteractions().seenOutgoing(peer, emoticon);
}
void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
switch (updates.type()) {
case mtpc_updateShortMessage: {

View File

@@ -21,6 +21,10 @@ namespace Main {
class Session;
} // namespace Main
namespace ChatHelpers {
struct EmojiInteractionsBunch;
} // namespace ChatHelpers
namespace Api {
class Updates final {
@@ -139,6 +143,21 @@ private:
MsgId rootId,
PeerId fromId,
const MTPSendMessageAction &action);
void handleEmojiInteraction(
not_null<PeerData*> peer,
const MTPDsendMessageEmojiInteraction &data);
void handleSpeakingInCall(
not_null<PeerData*> peer,
PeerId participantPeerId,
PeerData *participantPeerLoaded);
void handleEmojiInteraction(
not_null<PeerData*> peer,
MsgId messageId,
const QString &emoticon,
ChatHelpers::EmojiInteractionsBunch bunch);
void handleEmojiInteraction(
not_null<PeerData*> peer,
const QString &emoticon);
const not_null<Main::Session*> _session;

View File

@@ -0,0 +1,134 @@
/*
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_views.h"
#include "apiwrap.h"
#include "data/data_peer.h"
#include "data/data_peer_id.h"
#include "data/data_session.h"
#include "history/history.h"
#include "history/history_item.h"
#include "main/main_session.h"
namespace Api {
namespace {
// Send channel views each second.
constexpr auto kSendViewsTimeout = crl::time(1000);
} // namespace
ViewsManager::ViewsManager(not_null<ApiWrap*> api)
: _session(&api->session())
, _api(&api->instance()) {
}
void ViewsManager::scheduleIncrement(not_null<HistoryItem*> item) {
auto peer = item->history()->peer;
auto i = _incremented.find(peer);
if (i != _incremented.cend()) {
if (i->second.contains(item->id)) {
return;
}
} else {
i = _incremented.emplace(peer).first;
}
i->second.emplace(item->id);
auto j = _toIncrement.find(peer);
if (j == _toIncrement.cend()) {
j = _toIncrement.emplace(peer).first;
_incrementTimer.callOnce(kSendViewsTimeout);
}
j->second.emplace(item->id);
}
void ViewsManager::removeIncremented(not_null<PeerData*> peer) {
_incremented.remove(peer);
}
void ViewsManager::viewsIncrement() {
for (auto i = _toIncrement.begin(); i != _toIncrement.cend();) {
if (_incrementRequests.contains(i->first)) {
++i;
continue;
}
QVector<MTPint> ids;
ids.reserve(i->second.size());
for (const auto &msgId : i->second) {
ids.push_back(MTP_int(msgId));
}
const auto requestId = _api.request(MTPmessages_GetMessagesViews(
i->first->input,
MTP_vector<MTPint>(ids),
MTP_bool(true)
)).done([=](
const MTPmessages_MessageViews &result,
mtpRequestId requestId) {
done(ids, result, requestId);
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
fail(error, requestId);
}).afterDelay(5).send();
_incrementRequests.emplace(i->first, requestId);
i = _toIncrement.erase(i);
}
}
void ViewsManager::done(
QVector<MTPint> ids,
const MTPmessages_MessageViews &result,
mtpRequestId requestId) {
const auto &data = result.c_messages_messageViews();
auto &owner = _session->data();
owner.processUsers(data.vusers());
owner.processChats(data.vchats());
auto &v = data.vviews().v;
if (ids.size() == v.size()) {
for (const auto &[peer, id] : _incrementRequests) {
if (id != requestId) {
continue;
}
const auto channel = peerToChannel(peer->id);
for (auto j = 0, l = int(ids.size()); j < l; ++j) {
if (const auto item = owner.message(channel, ids[j].v)) {
v[j].match([&](const MTPDmessageViews &data) {
if (const auto views = data.vviews()) {
item->setViewsCount(views->v);
}
if (const auto forwards = data.vforwards()) {
item->setForwardsCount(forwards->v);
}
if (const auto replies = data.vreplies()) {
item->setReplies(*replies);
}
});
}
}
_incrementRequests.erase(peer);
break;
}
}
if (!_toIncrement.empty() && !_incrementTimer.isActive()) {
_incrementTimer.callOnce(kSendViewsTimeout);
}
}
void ViewsManager::fail(const MTP::Error &error, mtpRequestId requestId) {
for (const auto &[peer, id] : _incrementRequests) {
if (id == requestId) {
_incrementRequests.erase(peer);
break;
}
}
if (!_toIncrement.empty() && !_incrementTimer.isActive()) {
_incrementTimer.callOnce(kSendViewsTimeout);
}
}
} // namespace Api

View File

@@ -0,0 +1,49 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "mtproto/sender.h"
#include "base/timer.h"
class ApiWrap;
class PeerData;
namespace Main {
class Session;
} // namespace Main
namespace Api {
class ViewsManager final {
public:
explicit ViewsManager(not_null<ApiWrap*> api);
void scheduleIncrement(not_null<HistoryItem*> item);
void removeIncremented(not_null<PeerData*> peer);
private:
void viewsIncrement();
void done(
QVector<MTPint> ids,
const MTPmessages_MessageViews &result,
mtpRequestId requestId);
void fail(const MTP::Error &error, mtpRequestId requestId);
const not_null<Main::Session*> _session;
MTP::Sender _api;
base::flat_map<not_null<PeerData*>, base::flat_set<MsgId>> _incremented;
base::flat_map<not_null<PeerData*>, base::flat_set<MsgId>> _toIncrement;
base::flat_map<not_null<PeerData*>, mtpRequestId> _incrementRequests;
base::flat_map<mtpRequestId, not_null<PeerData*>> _incrementByRequest;
base::Timer _incrementTimer;
};
} // namespace Api

View File

@@ -130,7 +130,6 @@ struct State {
not_null<HistoryItem*> item,
not_null<QWidget*> context) {
auto weak = QPointer<QWidget>(context.get());
const auto fullId = item->fullId();
const auto session = &item->history()->session();
return [=](auto consumer) {
if (!weak) {
@@ -203,7 +202,7 @@ bool UpdateUserpics(
}
auto &was = state->userpics;
auto now = std::vector<Userpic>();
for (const auto peer : peers) {
for (const auto &peer : peers) {
if (ranges::contains(now, peer, &Userpic::peer)) {
continue;
}
@@ -356,6 +355,8 @@ rpl::producer<Ui::WhoReadContent> WhoRead(
} else if (UpdateUserpics(state, item, peers)) {
RegenerateParticipants(state, small, large);
pushNext();
} else if (peers.empty()) {
pushNext();
}
}, lifetime);

View File

@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_global_privacy.h"
#include "api/api_updates.h"
#include "api/api_user_privacy.h"
#include "api/api_views.h"
#include "data/stickers/data_stickers.h"
#include "data/data_drafts.h"
#include "data/data_changes.h"
@@ -46,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/core_cloud_password.h"
#include "core/application.h"
#include "base/unixtime.h"
#include "base/random.h"
#include "base/qt_adapters.h"
#include "base/call_delayed.h"
#include "lang/lang_keys.h"
@@ -140,7 +142,8 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
, _sensitiveContent(std::make_unique<Api::SensitiveContent>(this))
, _globalPrivacy(std::make_unique<Api::GlobalPrivacy>(this))
, _userPrivacy(std::make_unique<Api::UserPrivacy>(this))
, _inviteLinks(std::make_unique<Api::InviteLinks>(this)) {
, _inviteLinks(std::make_unique<Api::InviteLinks>(this))
, _views(std::make_unique<Api::ViewsManager>(this)) {
crl::on_main(session, [=] {
// You can't use _session->lifetime() in the constructor,
// only queued, because it is not constructed yet.
@@ -557,7 +560,8 @@ void ApiWrap::resolveMessageDatas() {
)).done([=](
const MTPmessages_Messages &result,
mtpRequestId requestId) {
gotMessageDatas(nullptr, result, requestId);
_session->data().processExistingMessages(nullptr, result);
finalizeMessageDataRequest(nullptr, requestId);
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
finalizeMessageDataRequest(nullptr, requestId);
}).afterDelay(kSmallDelayMs).send();
@@ -583,7 +587,8 @@ void ApiWrap::resolveMessageDatas() {
)).done([=](
const MTPmessages_Messages &result,
mtpRequestId requestId) {
gotMessageDatas(channel, result, requestId);
_session->data().processExistingMessages(channel, result);
finalizeMessageDataRequest(channel, requestId);
}).fail([=](const MTP::Error &error, mtpRequestId requestId) {
finalizeMessageDataRequest(channel, requestId);
}).afterDelay(kSmallDelayMs).send();
@@ -599,37 +604,6 @@ void ApiWrap::resolveMessageDatas() {
}
}
void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId requestId) {
const auto handleResult = [&](auto &&result) {
_session->data().processUsers(result.vusers());
_session->data().processChats(result.vchats());
_session->data().processMessages(
result.vmessages(),
NewMessageType::Existing);
};
switch (msgs.type()) {
case mtpc_messages_messages:
handleResult(msgs.c_messages_messages());
break;
case mtpc_messages_messagesSlice:
handleResult(msgs.c_messages_messagesSlice());
break;
case mtpc_messages_channelMessages: {
auto &d = msgs.c_messages_channelMessages();
if (channel) {
channel->ptsReceived(d.vpts().v);
} else {
LOG(("App Error: received messages.channelMessages when no channel was passed! (ApiWrap::gotDependencyItem)"));
}
handleResult(d);
} break;
case mtpc_messages_messagesNotModified:
LOG(("API Error: received messages.messagesNotModified! (ApiWrap::gotDependencyItem)"));
break;
}
finalizeMessageDataRequest(channel, requestId);
}
void ApiWrap::finalizeMessageDataRequest(
ChannelData *channel,
mtpRequestId requestId) {
@@ -661,8 +635,8 @@ QString ApiWrap::exportDirectMessageLink(
const auto fallback = [&] {
auto linkChannel = channel;
auto linkItemId = item->id;
auto linkCommentId = 0;
auto linkThreadId = 0;
auto linkCommentId = MsgId();
auto linkThreadId = MsgId();
if (inRepliesContext) {
if (const auto rootId = item->replyToTop()) {
const auto root = item->history()->owner().message(
@@ -692,11 +666,11 @@ QString ApiWrap::exportDirectMessageLink(
: "c/" + QString::number(peerToChannel(linkChannel->id).bare);
const auto query = base
+ '/'
+ QString::number(linkItemId)
+ QString::number(linkItemId.bare)
+ (linkCommentId
? "?comment=" + QString::number(linkCommentId)
? "?comment=" + QString::number(linkCommentId.bare)
: linkThreadId
? "?thread=" + QString::number(linkThreadId)
? "?thread=" + QString::number(linkThreadId.bare)
: "");
if (linkChannel->hasUsername()
&& !linkChannel->isMegagroup()
@@ -1758,7 +1732,7 @@ void ApiWrap::deleteAllFromUser(
? history->collectMessagesFromUserToDelete(from)
: QVector<MsgId>();
const auto channelId = peerToChannel(channel->id);
for (const auto msgId : ids) {
for (const auto &msgId : ids) {
if (const auto item = _session->data().message(channelId, msgId)) {
item->destroy();
}
@@ -3652,8 +3626,9 @@ void ApiWrap::forwardMessages(
const auto history = action.history;
const auto peer = history->peer;
histories.readInbox(history);
if (!action.options.scheduled) {
histories.readInbox(history);
}
const auto anonymousPost = peer->amAnonymous();
const auto silentPost = ShouldSendSilent(peer, action.options);
@@ -3724,7 +3699,7 @@ void ApiWrap::forwardMessages(
ids.reserve(count);
randomIds.reserve(count);
for (const auto item : draft.items) {
const auto randomId = openssl::RandomValue<uint64>();
const auto randomId = base::RandomValue<uint64>();
if (genClientSideMessage) {
if (const auto message = item->toHistoryMessage()) {
const auto newId = FullMsgId(
@@ -3835,7 +3810,7 @@ void ApiWrap::sendSharedContact(
MTP_string(lastName),
MTP_string(), // vcard
MTP_long(userId.bare)),
MTPReplyMarkup());
HistoryMessageMarkupData());
const auto media = MTP_inputMediaContact(
MTP_string(phone),
@@ -4034,7 +4009,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
auto newId = FullMsgId(
peerToChannel(peer->id),
_session->data().nextLocalMessageId());
auto randomId = openssl::RandomValue<uint64>();
auto randomId = base::RandomValue<uint64>();
TextUtilities::Trim(sending);
@@ -4099,7 +4074,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
messagePostAuthor,
sending,
media,
MTPReplyMarkup());
HistoryMessageMarkupData());
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
history->sendRequestId = request(MTPmessages_SendMessage(
MTP_flags(sendFlags),
@@ -4158,7 +4133,7 @@ void ApiWrap::sendBotStart(not_null<UserData*> bot, PeerData *chat) {
sendMessage(std::move(message));
return;
}
const auto randomId = openssl::RandomValue<uint64>();
const auto randomId = base::RandomValue<uint64>();
request(MTPmessages_StartBot(
bot->inputUser,
chat ? chat->input : MTP_inputPeerEmpty(),
@@ -4184,7 +4159,7 @@ void ApiWrap::sendInlineResult(
const auto newId = FullMsgId(
peerToChannel(peer->id),
_session->data().nextLocalMessageId());
const auto randomId = openssl::RandomValue<uint64>();
const auto randomId = base::RandomValue<uint64>();
auto flags = NewMessageFlags(peer);
auto sendFlags = MTPmessages_SendInlineBotResult::Flag::f_clear_draft | 0;
@@ -4340,7 +4315,7 @@ void ApiWrap::sendMedia(
not_null<HistoryItem*> item,
const MTPInputMedia &media,
Api::SendOptions options) {
const auto randomId = openssl::RandomValue<uint64>();
const auto randomId = base::RandomValue<uint64>();
_session->data().registerMessageRandomId(randomId, item->fullId());
sendMediaWithRandomId(item, media, options, randomId);
@@ -4414,7 +4389,7 @@ void ApiWrap::sendAlbumWithUploaded(
const MessageGroupId &groupId,
const MTPInputMedia &media) {
const auto localId = item->fullId();
const auto randomId = openssl::RandomValue<uint64>();
const auto randomId = base::RandomValue<uint64>();
_session->data().registerMessageRandomId(randomId, localId);
const auto albumIt = _sendingAlbums.find(groupId.raw());
@@ -4731,6 +4706,10 @@ Api::InviteLinks &ApiWrap::inviteLinks() {
return *_inviteLinks;
}
Api::ViewsManager &ApiWrap::views() {
return *_views;
}
void ApiWrap::createPoll(
const PollData &data,
const SendAction &action,
@@ -4768,7 +4747,7 @@ void ApiWrap::createPoll(
MTP_int(replyTo),
PollDataToInputMedia(&data),
MTP_string(),
MTP_long(openssl::RandomValue<uint64>()),
MTP_long(base::RandomValue<uint64>()),
MTPReplyMarkup(),
MTPVector<MTPMessageEntity>(),
MTP_int(action.options.scheduled)

View File

@@ -62,6 +62,7 @@ class SensitiveContent;
class GlobalPrivacy;
class UserPrivacy;
class InviteLinks;
class ViewsManager;
namespace details {
@@ -400,6 +401,7 @@ public:
[[nodiscard]] Api::GlobalPrivacy &globalPrivacy();
[[nodiscard]] Api::UserPrivacy &userPrivacy();
[[nodiscard]] Api::InviteLinks &inviteLinks();
[[nodiscard]] Api::ViewsManager &views();
void createPoll(
const PollData &data,
@@ -456,7 +458,6 @@ private:
void saveDraftsToCloud();
void resolveMessageDatas();
void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId requestId);
void finalizeMessageDataRequest(
ChannelData *channel,
mtpRequestId requestId);
@@ -720,6 +721,7 @@ private:
const std::unique_ptr<Api::GlobalPrivacy> _globalPrivacy;
const std::unique_ptr<Api::UserPrivacy> _userPrivacy;
const std::unique_ptr<Api::InviteLinks> _inviteLinks;
const std::unique_ptr<Api::ViewsManager> _views;
base::flat_map<FullMsgId, mtpRequestId> _pollVotesRequestIds;
base::flat_map<FullMsgId, mtpRequestId> _pollCloseRequestIds;

View File

@@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "mtproto/sender.h"
#include "base/flat_set.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include "boxes/confirm_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/peers/add_participants_box.h"
@@ -383,7 +383,7 @@ void AddContactBox::save() {
lastName = QString();
}
_sentName = firstName;
_contactId = openssl::RandomValue<uint64>();
_contactId = base::RandomValue<uint64>();
_addRequest = _session->api().request(MTPcontacts_ImportContacts(
MTP_vector<MTPInputContact>(
1,

View File

@@ -210,7 +210,7 @@ void ServiceCheck::Generator::paintFrame(
const auto frames = framesForStyle(st);
auto &image = frames->image;
const auto count = int(frames->ready.size());
const auto index = int(std::round(toggled * (count - 1)));
const auto index = int(base::SafeRound(toggled * (count - 1)));
Assert(index >= 0 && index < count);
if (!frames->ready[index]) {
frames->ready[index] = true;
@@ -288,7 +288,6 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
bool out) {
Expects(history->peer->isUser());
static auto id = ServerMaxMsgId + (ServerMaxMsgId / 3);
const auto flags = MessageFlag::FakeHistoryItem
| MessageFlag::HasFromId
| (out ? MessageFlag::Outgoing : MessageFlag(0));
@@ -296,7 +295,7 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
const auto viaBotId = UserId();
const auto groupedId = uint64();
const auto item = history->makeMessage(
++id,
history->nextNonHistoryEntryId(),
flags,
replyTo,
viaBotId,
@@ -305,7 +304,7 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
QString(),
TextWithEntities{ TextUtilities::Clean(text) },
MTP_messageMediaEmpty(),
MTPReplyMarkup(),
HistoryMessageMarkupData(),
groupedId);
return AdminLog::OwnedItem(delegate, item);
}

View File

@@ -30,7 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unique_qptr.h"
#include "base/event_filter.h"
#include "base/call_delayed.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include "window/window_session_controller.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@@ -885,7 +885,7 @@ not_null<Ui::InputField*> CreatePollBox::setupSolution(
object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
using namespace Settings;
const auto id = openssl::RandomValue<uint64>();
const auto id = base::RandomValue<uint64>();
const auto error = lifetime().make_state<Errors>(Error::Question);
auto result = object_ptr<Ui::VerticalLayout>(this);

View File

@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/peer_list_controllers.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include "boxes/confirm_box.h"
#include "ui/widgets/checkbox.h"
#include "ui/ui_utility.h"
@@ -36,7 +36,7 @@ void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) {
auto &histories = history->owner().histories();
const auto requestType = Data::Histories::RequestType::Send;
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
const auto randomId = openssl::RandomValue<uint64>();
const auto randomId = base::RandomValue<uint64>();
const auto api = &chat->session().api();
history->sendRequestId = api->request(MTPmessages_SendMedia(
MTP_flags(0),

View File

@@ -453,7 +453,14 @@ void EditAdminBox::transferOwnership() {
)).fail([=](const MTP::Error &error) {
_checkTransferRequestId = 0;
if (!handleTransferPasswordError(error)) {
getDelegate()->show(Box<ConfirmBox>(
const auto box = std::make_shared<QPointer<ConfirmBox>>();
const auto callback = crl::guard(this, [=] {
transferOwnershipChecked();
if (*box) {
(*box)->closeBox();
}
});
*box = getDelegate()->show(Box<ConfirmBox>(
tr::lng_rights_transfer_about(
tr::now,
lt_group,
@@ -462,7 +469,7 @@ void EditAdminBox::transferOwnership() {
Ui::Text::Bold(user()->shortName()),
Ui::Text::RichLangValue),
tr::lng_rights_transfer_sure(tr::now),
crl::guard(this, [=] { transferOwnershipChecked(); })));
callback));
}
}).send();
}

View File

@@ -268,7 +268,8 @@ void Row::update(const InviteLinkData &data, TimeId now) {
void Row::updateExpireProgress(TimeId now) {
const auto updated = ComputeProgress(_data, now);
if (std::round(_progressTillExpire * 360) != std::round(updated * 360)) {
if (base::SafeRound(_progressTillExpire * 360)
!= base::SafeRound(updated * 360)) {
_progressTillExpire = updated;
const auto color = ComputeColor(_data, _progressTillExpire);
if (_color != color) {
@@ -291,7 +292,8 @@ crl::time Row::updateExpireIn() const {
if (_data.expireDate <= start) {
return 0;
}
return std::round((_data.expireDate - start) * crl::time(1000) / 720.);
return base::SafeRound(
(_data.expireDate - start) * crl::time(1000) / 720.);
}
QString Row::generateName() {

View File

@@ -549,9 +549,6 @@ void SendFilesBox::pushBlock(int from, int till) {
block.takeWidget(),
QMargins(0, _inner->count() ? st::sendMediaRowSkip : 0, 0, 0));
const auto preventDelete =
widget->lifetime().make_state<rpl::event_stream<int>>();
block.itemDeleteRequest(
) | rpl::filter([=] {
return !_removingIndex;
@@ -562,9 +559,9 @@ void SendFilesBox::pushBlock(int from, int till) {
if (index < 0 || index >= _list.files.size()) {
return;
}
// Prevent item delete if it is the only one.
// Just close the box if it is the only one.
if (_list.files.size() == 1) {
preventDelete->fire_copy(0);
closeBox();
return;
}
_list.files.erase(_list.files.begin() + index);
@@ -572,9 +569,7 @@ void SendFilesBox::pushBlock(int from, int till) {
});
}, widget->lifetime());
rpl::merge(
block.itemReplaceRequest(),
preventDelete->events()
block.itemReplaceRequest(
) | rpl::start_with_next([=](int index) {
const auto replace = [=](Ui::PreparedList list) {
if (list.files.empty()) {

View File

@@ -1113,7 +1113,7 @@ QString AppendShareGameScoreUrl(
auto channelAccessHash = uint64(channel ? channel->access : 0);
shareHashDataInts[0] = session->userId().bare;
shareHashDataInts[1] = fullId.channel.bare;
shareHashDataInts[2] = fullId.msg;
shareHashDataInts[2] = uint64(fullId.msg.bare);
shareHashDataInts[3] = channelAccessHash;
// Count SHA1() of data.
@@ -1200,7 +1200,6 @@ void ShareGameScoreByHash(
//}
if (((hashDataInts[1] >> 40) != 0)
|| ((hashDataInts[2] >> 32) != 0)
|| (!hashDataInts[1] && channelAccessHash)) {
// If there is no channel id, there should be no channel access_hash.
Ui::show(Box<InformBox>(tr::lng_share_wrong_user(tr::now)));
@@ -1208,7 +1207,7 @@ void ShareGameScoreByHash(
}
auto channelId = ChannelId(hashDataInts[1]);
auto msgId = MsgId(hashDataInts[2]);
auto msgId = MsgId(int64(hashDataInts[2]));
if (const auto item = session->data().message(channelId, msgId)) {
FastShareMessage(item);
} else {

View File

@@ -17,7 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "mtproto/sender.h"
#include "storage/storage_account.h"
#include "dialogs/dialogs_layout.h"
#include "dialogs/ui/dialogs_layout.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/image/image.h"

View File

@@ -21,7 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/sticker_set_box.h"
#include "apiwrap.h"
#include "storage/storage_account.h"
#include "dialogs/dialogs_layout.h"
#include "dialogs/ui/dialogs_layout.h"
#include "lottie/lottie_single_player.h"
#include "chat_helpers/stickers_lottie.h"
#include "ui/widgets/buttons.h"
@@ -64,7 +64,7 @@ private:
void setCounter(int counter);
QString _text;
Dialogs::Layout::UnreadBadgeStyle _st;
Dialogs::Ui::UnreadBadgeStyle _st;
};
@@ -303,7 +303,7 @@ StickersBox::CounterWidget::CounterWidget(
: RpWidget(parent) {
setAttribute(Qt::WA_TransparentForMouseEvents);
_st.sizeId = Dialogs::Layout::UnreadBadgeInStickersBox;
_st.sizeId = Dialogs::Ui::UnreadBadgeInStickersBox;
_st.textTop = st::stickersFeaturedBadgeTextTop;
_st.size = st::stickersFeaturedBadgeSize;
_st.padding = st::stickersFeaturedBadgePadding;
@@ -323,7 +323,7 @@ void StickersBox::CounterWidget::setCounter(int counter) {
Painter p(&dummy);
auto newWidth = 0;
Dialogs::Layout::paintUnreadCount(p, _text, 0, 0, _st, &newWidth);
Dialogs::Ui::paintUnreadCount(p, _text, 0, 0, _st, &newWidth);
resize(newWidth, st::stickersFeaturedBadgeSize);
}
@@ -334,7 +334,7 @@ void StickersBox::CounterWidget::paintEvent(QPaintEvent *e) {
if (!_text.isEmpty()) {
auto unreadRight = rtl() ? 0 : width();
auto unreadTop = 0;
Dialogs::Layout::paintUnreadCount(p, _text, unreadRight, unreadTop, _st);
Dialogs::Ui::paintUnreadCount(p, _text, unreadRight, unreadTop, _st);
}
}

View File

@@ -141,7 +141,7 @@ private:
};
BoxController::Row::Row(not_null<HistoryItem*> item)
: PeerListRow(item->history()->peer, item->id)
: PeerListRow(item->history()->peer, item->id.bare)
, _items(1, item)
, _date(ItemDateTime(item).date())
, _type(ComputeType(item))

View File

@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/rate_call_box.h"
#include "calls/calls_instance.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include "mtproto/mtproto_dh_utils.h"
#include "mtproto/mtproto_config.h"
#include "core/application.h"
@@ -233,7 +234,7 @@ void Call::startOutgoing() {
_api.request(MTPphone_RequestCall(
MTP_flags(flags),
_user->inputUser,
MTP_int(openssl::RandomValue<int32>()),
MTP_int(base::RandomValue<int32>()),
MTP_bytes(_gaHash),
MTP_phoneCallProtocol(
MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p

View File

@@ -209,7 +209,7 @@ void VideoBubble::updateSizeToFrame(QSize frame) {
size = frame.scaled((_min + _max) / 2, Qt::KeepAspectRatio);
} else {
const auto area = size.width() * size.height();
const auto w = int(std::round(std::max(
const auto w = int(base::SafeRound(std::max(
std::sqrt((frame.width() * float64(area)) / (frame.height() * 1.)),
1.)));
const auto h = area / w;

View File

@@ -27,7 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer_values.h"
#include "data/data_session.h"
#include "base/global_shortcuts.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include "webrtc/webrtc_video_track.h"
#include "webrtc/webrtc_media_devices.h"
#include "webrtc/webrtc_create_adm.h"
@@ -74,6 +74,11 @@ constexpr auto kMaxMediumQualities = 16; // 4 Fulls or 16 Mediums.
return msgId / double(1ULL << 32);
}
[[nodiscard]] int64 TimestampInMsFromMsgId(mtpMsgId msgId) {
// return (msgId * 1000) / (1ULL << 32); // Almost... But this overflows.
return ((msgId / (1ULL << 10)) * 1000) / (1ULL << 22);
}
[[nodiscard]] uint64 FindLocalRaisedHandRating(
const std::vector<Data::GroupCallParticipant> &list) {
const auto i = ranges::max_element(
@@ -95,6 +100,41 @@ using JoinClientFields = std::variant<
JoinVideoEndpoint,
JoinBroadcastStream>;
class RequestCurrentTimeTask final : public tgcalls::BroadcastPartTask {
public:
RequestCurrentTimeTask(
base::weak_ptr<GroupCall> call,
Fn<void(int64)> done);
void done(int64 value);
void cancel() override;
private:
const base::weak_ptr<GroupCall> _call;
Fn<void(int64)> _done;
QMutex _mutex;
};
RequestCurrentTimeTask::RequestCurrentTimeTask(
base::weak_ptr<GroupCall> call,
Fn<void(int64)> done)
: _call(call)
, _done(std::move(done)) {
}
void RequestCurrentTimeTask::done(int64 value) {
QMutexLocker lock(&_mutex);
if (_done) {
base::take(_done)(value);
}
}
void RequestCurrentTimeTask::cancel() {
QMutexLocker lock(&_mutex);
_done = nullptr;
}
[[nodiscard]] JoinClientFields ParseJoinResponse(const QByteArray &json) {
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
const auto document = QJsonDocument::fromJson(json, &error);
@@ -968,7 +1008,7 @@ void GroupCall::start(TimeId scheduleDate) {
_createRequestId = _api.request(MTPphone_CreateGroupCall(
MTP_flags(scheduleDate ? Flag::f_schedule_date : Flag(0)),
_peer->input,
MTP_int(openssl::RandomValue<int32>()),
MTP_int(base::RandomValue<int32>()),
MTPstring(), // title
MTP_int(scheduleDate)
)).done([=](const MTPUpdates &result) {
@@ -1273,7 +1313,12 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
joinAs()->input,
MTP_string(_joinHash),
MTP_dataJSON(MTP_bytes(json))
)).done([=](const MTPUpdates &updates) {
)).done([=](
const MTPUpdates &updates,
const MTP::Response &response) {
_serverTimeMs = TimestampInMsFromMsgId(response.outerMsgId);
_serverTimeMsGotAt = crl::now();
_joinState.finish(ssrc);
_mySsrcs.emplace(ssrc);
@@ -2253,6 +2298,16 @@ bool GroupCall::tryCreateController() {
.createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator(
settings.callAudioBackend()),
.videoCapture = _cameraCapture,
.requestCurrentTime = [=, call = base::make_weak(this)](
std::function<void(int64_t)> done) {
auto result = std::make_shared<RequestCurrentTimeTask>(
call,
std::move(done));
crl::on_main(weak, [=] {
result->done(approximateServerTimeInMs());
});
return result;
},
.requestAudioBroadcastPart = [=, call = base::make_weak(this)](
int64_t time,
int64_t period,
@@ -2499,6 +2554,12 @@ void GroupCall::mediaChannelDescriptionsCancel(
}
}
int64 GroupCall::approximateServerTimeInMs() const {
Expects(_serverTimeMs != 0);
return _serverTimeMs + (crl::now() - _serverTimeMsGotAt);
}
void GroupCall::updateRequestedVideoChannels() {
_requestedVideoChannelsUpdateScheduled = false;
const auto real = lookupReal();

View File

@@ -406,16 +406,6 @@ public:
private:
class LoadPartTask;
class MediaChannelDescriptionsTask;
public:
void broadcastPartStart(std::shared_ptr<LoadPartTask> task);
void broadcastPartCancel(not_null<LoadPartTask*> task);
void mediaChannelDescriptionsStart(
std::shared_ptr<MediaChannelDescriptionsTask> task);
void mediaChannelDescriptionsCancel(
not_null<MediaChannelDescriptionsTask*> task);
private:
using GlobalShortcutValue = base::GlobalShortcutValue;
using Error = Group::Error;
struct SinkPointer;
@@ -464,6 +454,14 @@ private:
return true;
}
void broadcastPartStart(std::shared_ptr<LoadPartTask> task);
void broadcastPartCancel(not_null<LoadPartTask*> task);
void mediaChannelDescriptionsStart(
std::shared_ptr<MediaChannelDescriptionsTask> task);
void mediaChannelDescriptionsCancel(
not_null<MediaChannelDescriptionsTask*> task);
[[nodiscard]] int64 approximateServerTimeInMs() const;
[[nodiscard]] bool mediaChannelDescriptionsFill(
not_null<MediaChannelDescriptionsTask*> task,
Fn<bool(uint32)> resolved = nullptr);
@@ -571,11 +569,14 @@ private:
base::flat_set<
std::shared_ptr<
MediaChannelDescriptionsTask>,
base::pointer_comparator<MediaChannelDescriptionsTask>> _mediaChannelDescriptionses;
base::pointer_comparator<
MediaChannelDescriptionsTask>> _mediaChannelDescriptionses;
rpl::variable<not_null<PeerData*>> _joinAs;
std::vector<not_null<PeerData*>> _possibleJoinAs;
QString _joinHash;
int64 _serverTimeMs = 0;
crl::time _serverTimeMsGotAt = 0;
rpl::variable<MuteState> _muted = MuteState::Muted;
rpl::variable<bool> _canManage = false;

View File

@@ -57,7 +57,7 @@ auto RowBlobs() -> std::array<Ui::Paint::Blobs::BlobData, 2> {
}
[[nodiscard]] QString StatusPercentString(float volume) {
return QString::number(int(std::round(volume * 200))) + '%';
return QString::number(int(base::SafeRound(volume * 200))) + '%';
}
[[nodiscard]] int StatusPercentWidth(const QString &percent) {
@@ -492,7 +492,7 @@ int MembersRow::statusIconWidth(bool skipIcon) const {
const auto full = iconWidth
+ _statusIcon->percentWidth
+ st::normalFont->spacew;
return int(std::round(shown * full));
return int(base::SafeRound(shown * full));
}
int MembersRow::statusIconHeight() const {

View File

@@ -1000,6 +1000,8 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
_recordingMark->setClickedCallback([=] {
showToast({ (livestream
? tr::lng_group_call_is_recorded_channel
: real->recordVideo()
? tr::lng_group_call_is_recorded_video
: tr::lng_group_call_is_recorded)(tr::now) });
});
const auto animate = [=] {

View File

@@ -461,15 +461,15 @@ Viewport::Layout Viewport::countWide(int outerWidth, int outerHeight) const {
const auto columns = slices;
const auto sizew = (outerWidth + skip) / float64(columns);
for (auto column = 0; column != columns; ++column) {
const auto left = int(std::round(column * sizew));
const auto width = int(std::round(column * sizew + sizew - skip))
- left;
const auto rows = int(std::round((count - index)
const auto left = int(base::SafeRound(column * sizew));
const auto width = int(
base::SafeRound(column * sizew + sizew - skip)) - left;
const auto rows = int(base::SafeRound((count - index)
/ float64(columns - column)));
const auto sizeh = (outerHeight + skip) / float64(rows);
for (auto row = 0; row != rows; ++row) {
const auto top = int(std::round(row * sizeh));
const auto height = int(std::round(
const auto top = int(base::SafeRound(row * sizeh));
const auto height = int(base::SafeRound(
row * sizeh + sizeh - skip)) - top;
auto &geometry = sizes[index];
geometry.columns = {
@@ -493,15 +493,15 @@ Viewport::Layout Viewport::countWide(int outerWidth, int outerHeight) const {
const auto rows = slices;
const auto sizeh = (outerHeight + skip) / float64(rows);
for (auto row = 0; row != rows; ++row) {
const auto top = int(std::round(row * sizeh));
const auto height = int(std::round(row * sizeh + sizeh - skip))
- top;
const auto columns = int(std::round((count - index)
const auto top = int(base::SafeRound(row * sizeh));
const auto height = int(
base::SafeRound(row * sizeh + sizeh - skip)) - top;
const auto columns = int(base::SafeRound((count - index)
/ float64(rows - row)));
const auto sizew = (outerWidth + skip) / float64(columns);
for (auto column = 0; column != columns; ++column) {
const auto left = int(std::round(column * sizew));
const auto width = int(std::round(
const auto left = int(base::SafeRound(column * sizew));
const auto width = int(base::SafeRound(
column * sizew + sizew - skip)) - left;
auto &geometry = sizes[index];
geometry.rows = {

View File

@@ -242,7 +242,7 @@ vec4 background() {
QSize outer,
float factor) {
factor *= kBlurTextureSizeFactor;
const auto area = outer / int(std::round(factor * cScale() / 100));
const auto area = outer / int(base::SafeRound(factor * cScale() / 100));
const auto scaled = unscaled.scaled(area, Qt::KeepAspectRatio);
return (scaled.width() > unscaled.width()
|| scaled.height() > unscaled.height())

View File

@@ -35,6 +35,14 @@ Viewport::VideoTile::VideoTile(
Expects(track.track != nullptr);
Expects(track.row != nullptr);
using namespace rpl::mappers;
_track.track->stateValue(
) | rpl::filter(
_1 == Webrtc::VideoState::Paused
) | rpl::take(1) | rpl::start_with_next([=] {
_wasPaused = true;
}, _lifetime);
setup(std::move(pinned));
}
@@ -68,11 +76,8 @@ QSize Viewport::VideoTile::PausedVideoSize() {
QSize Viewport::VideoTile::trackOrUserpicSize() const {
if (const auto size = trackSize(); !size.isEmpty()) {
return size;
} else if (_userpicSize.isEmpty()
&& _track.track->state() == Webrtc::VideoState::Paused) {
_userpicSize = PausedVideoSize();
}
return _userpicSize;
return _wasPaused ? PausedVideoSize() : QSize();
}
bool Viewport::VideoTile::screencast() const {

View File

@@ -110,12 +110,12 @@ private:
QRect _geometry;
TileAnimation _animation;
rpl::variable<QSize> _trackSize;
mutable QSize _userpicSize;
QRect _pinOuter;
QRect _pinInner;
QRect _backOuter;
QRect _backInner;
Ui::Animations::Simple _topControlsShownAnimation;
bool _wasPaused = false;
bool _topControlsShown = false;
bool _pinned = false;
bool _hidden = true;

View File

@@ -92,7 +92,7 @@ MenuVolumeItem::MenuVolumeItem(
const auto volume = _localMuted
? 0
: std::round(_slider->value() * kMaxVolumePercent);
: base::SafeRound(_slider->value() * kMaxVolumePercent);
const auto muteProgress =
_crossLineAnimation.value((!volume) ? 1. : 0.);
@@ -140,7 +140,7 @@ MenuVolumeItem::MenuVolumeItem(
};
_slider->setChangeFinishedCallback([=](float64 value) {
const auto newVolume = std::round(value * _maxVolume);
const auto newVolume = base::SafeRound(value * _maxVolume);
const auto muted = (value == 0);
if (!_cloudMuted && muted) {
@@ -175,7 +175,7 @@ MenuVolumeItem::MenuVolumeItem(
}
if (_waitingForUpdateVolume) {
const auto localVolume =
std::round(_slider->value() * _maxVolume);
base::SafeRound(_slider->value() * _maxVolume);
if ((localVolume != newVolume)
&& (_cloudVolume == newVolume)) {
_changeVolumeRequests.fire(int(localVolume));

View File

@@ -16,7 +16,7 @@ struct SendCommandRequest {
not_null<PeerData*> peer;
QString command;
FullMsgId context;
int replyTo = 0;
MsgId replyTo = 0;
};
[[nodiscard]] QString WrapCommandInChat(

View File

@@ -199,9 +199,9 @@ bool BotKeyboard::moderateKeyActivate(int key) {
if (const auto markup = item->Get<HistoryMessageReplyMarkup>()) {
if (key >= Qt::Key_1 && key <= Qt::Key_2) {
const auto index = int(key - Qt::Key_1);
if (!markup->rows.empty()
if (!markup->data.rows.empty()
&& index >= 0
&& index < int(markup->rows.front().size())) {
&& index < int(markup->data.rows.front().size())) {
App::activateBotCommand(_controller, item, 0, index);
return true;
}
@@ -257,14 +257,14 @@ bool BotKeyboard::updateMarkup(HistoryItem *to, bool force) {
_singleUse = _forceReply || (markupFlags & ReplyMarkupFlag::SingleUse);
if (const auto markup = to->Get<HistoryMessageReplyMarkup>()) {
_placeholder = markup->placeholder;
_placeholder = markup->data.placeholder;
} else {
_placeholder = QString();
}
_impl = nullptr;
if (auto markup = to->Get<HistoryMessageReplyMarkup>()) {
if (!markup->rows.empty()) {
if (!markup->data.rows.empty()) {
_impl = std::make_unique<ReplyKeyboard>(
to,
std::make_unique<Style>(this, *_st));

View File

@@ -0,0 +1,528 @@
/*
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 "chat_helpers/emoji_interactions.h"
#include "chat_helpers/stickers_emoji_pack.h"
#include "history/history_item.h"
#include "history/history.h"
#include "history/view/history_view_element.h"
#include "history/view/media/history_view_sticker.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_changes.h"
#include "data/data_peer.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "ui/emoji_config.h"
#include "base/random.h"
#include "apiwrap.h"
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonValue>
namespace ChatHelpers {
namespace {
constexpr auto kMinDelay = crl::time(200);
constexpr auto kAccumulateDelay = crl::time(1000);
constexpr auto kAccumulateSeenRequests = kAccumulateDelay;
constexpr auto kAcceptSeenSinceRequest = 3 * crl::time(1000);
constexpr auto kMaxDelay = 2 * crl::time(1000);
constexpr auto kTimeNever = std::numeric_limits<crl::time>::max();
constexpr auto kJsonVersion = 1;
} // namespace
auto EmojiInteractions::Combine(CheckResult a, CheckResult b) -> CheckResult {
return {
.nextCheckAt = std::min(a.nextCheckAt, b.nextCheckAt),
.waitingForDownload = a.waitingForDownload || b.waitingForDownload,
};
}
EmojiInteractions::EmojiInteractions(not_null<Main::Session*> session)
: _session(session)
, _checkTimer([=] { check(); }) {
_session->changes().messageUpdates(
Data::MessageUpdate::Flag::Destroyed
| Data::MessageUpdate::Flag::Edited
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
if (update.flags & Data::MessageUpdate::Flag::Destroyed) {
_outgoing.remove(update.item);
_incoming.remove(update.item);
} else if (update.flags & Data::MessageUpdate::Flag::Edited) {
checkEdition(update.item, _outgoing);
checkEdition(update.item, _incoming);
}
}, _lifetime);
}
EmojiInteractions::~EmojiInteractions() = default;
void EmojiInteractions::checkEdition(
not_null<HistoryItem*> item,
base::flat_map<not_null<HistoryItem*>, std::vector<Animation>> &map) {
const auto i = map.find(item);
if (i != end(map)
&& (i->second.front().emoji != chooseInteractionEmoji(item))) {
map.erase(i);
}
}
EmojiPtr EmojiInteractions::chooseInteractionEmoji(
not_null<HistoryItem*> item) const {
return chooseInteractionEmoji(item->originalText().text);
}
EmojiPtr EmojiInteractions::chooseInteractionEmoji(
const QString &emoticon) const {
const auto emoji = Ui::Emoji::Find(emoticon);
if (!emoji) {
return nullptr;
}
const auto &pack = _session->emojiStickersPack();
if (!pack.animationsForEmoji(emoji).empty()) {
return emoji;
}
if (const auto original = emoji->original(); original != emoji) {
if (!pack.animationsForEmoji(original).empty()) {
return original;
}
}
static const auto kHearts = {
QString::fromUtf8("\xf0\x9f\x92\x9b"),
QString::fromUtf8("\xf0\x9f\x92\x99"),
QString::fromUtf8("\xf0\x9f\x92\x9a"),
QString::fromUtf8("\xf0\x9f\x92\x9c"),
QString::fromUtf8("\xf0\x9f\xa7\xa1"),
QString::fromUtf8("\xf0\x9f\x96\xa4"),
QString::fromUtf8("\xf0\x9f\xa4\x8e"),
QString::fromUtf8("\xf0\x9f\xa4\x8d"),
};
return ranges::contains(kHearts, emoji->id())
? Ui::Emoji::Find(QString::fromUtf8("\xe2\x9d\xa4"))
: emoji;
}
void EmojiInteractions::startOutgoing(
not_null<const HistoryView::Element*> view) {
const auto item = view->data();
if (!IsServerMsgId(item->id) || !item->history()->peer->isUser()) {
return;
}
const auto emoticon = item->originalText().text;
const auto emoji = chooseInteractionEmoji(emoticon);
if (!emoji) {
return;
}
const auto &pack = _session->emojiStickersPack();
const auto &list = pack.animationsForEmoji(emoji);
if (list.empty()) {
return;
}
auto &animations = _outgoing[item];
if (!animations.empty() && animations.front().emoji != emoji) {
// The message was edited, forget the old emoji.
animations.clear();
}
const auto last = !animations.empty() ? &animations.back() : nullptr;
const auto listSize = int(list.size());
const auto chooseDifferent = (last && listSize > 1);
const auto index = chooseDifferent
? base::RandomIndex(listSize - 1)
: base::RandomIndex(listSize);
const auto selected = (begin(list) + index)->second;
const auto document = (chooseDifferent && selected == last->document)
? (begin(list) + index + 1)->second
: selected;
const auto media = document->createMediaView();
media->checkStickerLarge();
const auto now = crl::now();
animations.push_back({
.emoticon = emoticon,
.emoji = emoji,
.document = document,
.media = media,
.scheduledAt = now,
.index = index,
});
check(now);
}
void EmojiInteractions::startIncoming(
not_null<PeerData*> peer,
MsgId messageId,
const QString &emoticon,
EmojiInteractionsBunch &&bunch) {
if (!peer->isUser()
|| bunch.interactions.empty()
|| !IsServerMsgId(messageId)) {
return;
}
const auto item = _session->data().message(nullptr, messageId);
if (!item) {
return;
}
const auto emoji = chooseInteractionEmoji(item);
if (!emoji || emoji != chooseInteractionEmoji(emoticon)) {
return;
}
const auto &pack = _session->emojiStickersPack();
const auto &list = pack.animationsForEmoji(emoji);
if (list.empty()) {
return;
}
auto &animations = _incoming[item];
if (!animations.empty() && animations.front().emoji != emoji) {
// The message was edited, forget the old emoji.
animations.clear();
}
const auto now = crl::now();
for (const auto &single : bunch.interactions) {
const auto at = now + crl::time(base::SafeRound(single.time * 1000));
if (!animations.empty() && animations.back().scheduledAt >= at) {
continue;
}
const auto listSize = int(list.size());
const auto index = (single.index - 1);
if (index < listSize) {
const auto document = (begin(list) + index)->second;
const auto media = document->createMediaView();
media->checkStickerLarge();
animations.push_back({
.emoticon = emoticon,
.emoji = emoji,
.document = document,
.media = media,
.scheduledAt = at,
.incoming = true,
.index = index,
});
}
}
if (animations.empty()) {
_incoming.remove(item);
} else {
check(now);
}
}
void EmojiInteractions::seenOutgoing(
not_null<PeerData*> peer,
const QString &emoticon) {
if (const auto i = _playsSent.find(peer); i != end(_playsSent)) {
if (const auto emoji = chooseInteractionEmoji(emoticon)) {
if (const auto j = i->second.find(emoji); j != end(i->second)) {
const auto last = j->second.lastDoneReceivedAt;
if (!last || last + kAcceptSeenSinceRequest > crl::now()) {
_seen.fire({ peer, emoticon });
}
}
}
}
}
auto EmojiInteractions::checkAnimations(crl::time now) -> CheckResult {
return Combine(
checkAnimations(now, _outgoing),
checkAnimations(now, _incoming));
}
auto EmojiInteractions::checkAnimations(
crl::time now,
base::flat_map<not_null<HistoryItem*>, std::vector<Animation>> &map
) -> CheckResult {
auto nearest = kTimeNever;
auto waitingForDownload = false;
for (auto i = begin(map); i != end(map);) {
auto lastStartedAt = crl::time();
auto &animations = i->second;
// Erase too old requests.
const auto j = ranges::find_if(animations, [&](const Animation &a) {
return !a.startedAt && (a.scheduledAt + kMaxDelay <= now);
});
if (j == begin(animations)) {
i = map.erase(i);
continue;
} else if (j != end(animations)) {
animations.erase(j, end(animations));
}
const auto item = i->first;
for (auto &animation : animations) {
if (animation.startedAt) {
lastStartedAt = animation.startedAt;
} else if (!animation.media->loaded()) {
animation.media->checkStickerLarge();
waitingForDownload = true;
break;
} else if (!lastStartedAt || lastStartedAt + kMinDelay <= now) {
animation.startedAt = now;
_playRequests.fire({
animation.emoticon,
item,
animation.media,
animation.scheduledAt,
animation.incoming,
});
break;
} else {
nearest = std::min(nearest, lastStartedAt + kMinDelay);
break;
}
}
++i;
}
return {
.nextCheckAt = nearest,
.waitingForDownload = waitingForDownload,
};
}
void EmojiInteractions::sendAccumulatedOutgoing(
crl::time now,
not_null<HistoryItem*> item,
std::vector<Animation> &animations) {
Expects(!animations.empty());
const auto firstStartedAt = animations.front().startedAt;
const auto intervalEnd = firstStartedAt + kAccumulateDelay;
if (intervalEnd > now) {
return;
}
const auto from = begin(animations);
const auto till = ranges::find_if(animations, [&](const auto &animation) {
return !animation.startedAt || (animation.startedAt >= intervalEnd);
});
auto bunch = EmojiInteractionsBunch();
bunch.interactions.reserve(till - from);
for (const auto &animation : ranges::make_subrange(from, till)) {
bunch.interactions.push_back({
.index = animation.index + 1,
.time = (animation.startedAt - firstStartedAt) / 1000.,
});
}
if (bunch.interactions.empty()) {
return;
}
const auto peer = item->history()->peer;
const auto emoji = from->emoji;
const auto requestId = _session->api().request(MTPmessages_SetTyping(
MTP_flags(0),
peer->input,
MTPint(), // top_msg_id
MTP_sendMessageEmojiInteraction(
MTP_string(from->emoticon),
MTP_int(item->id),
MTP_dataJSON(MTP_bytes(ToJson(bunch))))
)).done([=](const MTPBool &result, mtpRequestId requestId) {
auto &sent = _playsSent[peer][emoji];
if (sent.lastRequestId == requestId) {
sent.lastDoneReceivedAt = crl::now();
if (!_checkTimer.isActive()) {
_checkTimer.callOnce(kAcceptSeenSinceRequest);
}
}
}).send();
_playsSent[peer][emoji] = PlaySent{ .lastRequestId = requestId };
animations.erase(from, till);
}
void EmojiInteractions::clearAccumulatedIncoming(
crl::time now,
std::vector<Animation> &animations) {
Expects(!animations.empty());
const auto from = begin(animations);
const auto till = ranges::find_if(animations, [&](const auto &animation) {
return !animation.startedAt
|| (animation.startedAt + kMinDelay) > now;
});
animations.erase(from, till);
}
auto EmojiInteractions::checkAccumulated(crl::time now) -> CheckResult {
auto nearest = kTimeNever;
for (auto i = begin(_outgoing); i != end(_outgoing);) {
auto &[item, animations] = *i;
sendAccumulatedOutgoing(now, item, animations);
if (animations.empty()) {
i = _outgoing.erase(i);
continue;
} else if (const auto firstStartedAt = animations.front().startedAt) {
nearest = std::min(nearest, firstStartedAt + kAccumulateDelay);
Assert(nearest > now);
}
++i;
}
for (auto i = begin(_incoming); i != end(_incoming);) {
auto &animations = i->second;
clearAccumulatedIncoming(now, animations);
if (animations.empty()) {
i = _incoming.erase(i);
continue;
} else {
// Doesn't really matter when, just clear them finally.
nearest = std::min(nearest, now + kAccumulateDelay);
}
++i;
}
return {
.nextCheckAt = nearest,
};
}
void EmojiInteractions::check(crl::time now) {
if (!now) {
now = crl::now();
}
checkSeenRequests(now);
checkSentRequests(now);
const auto result1 = checkAnimations(now);
const auto result2 = checkAccumulated(now);
const auto result = Combine(result1, result2);
if (result.nextCheckAt < kTimeNever) {
Assert(result.nextCheckAt > now);
_checkTimer.callOnce(result.nextCheckAt - now);
} else if (!_playStarted.empty()) {
_checkTimer.callOnce(kAccumulateSeenRequests);
} else if (!_playsSent.empty()) {
_checkTimer.callOnce(kAcceptSeenSinceRequest);
}
setWaitingForDownload(result.waitingForDownload);
}
void EmojiInteractions::checkSeenRequests(crl::time now) {
for (auto i = begin(_playStarted); i != end(_playStarted);) {
auto &animations = i->second;
for (auto j = begin(animations); j != end(animations);) {
if (j->second + kAccumulateSeenRequests <= now) {
j = animations.erase(j);
} else {
++j;
}
}
if (animations.empty()) {
i = _playStarted.erase(i);
} else {
++i;
}
}
}
void EmojiInteractions::checkSentRequests(crl::time now) {
for (auto i = begin(_playsSent); i != end(_playsSent);) {
auto &animations = i->second;
for (auto j = begin(animations); j != end(animations);) {
const auto last = j->second.lastDoneReceivedAt;
if (last && last + kAcceptSeenSinceRequest <= now) {
j = animations.erase(j);
} else {
++j;
}
}
if (animations.empty()) {
i = _playsSent.erase(i);
} else {
++i;
}
}
}
void EmojiInteractions::setWaitingForDownload(bool waiting) {
if (_waitingForDownload == waiting) {
return;
}
_waitingForDownload = waiting;
if (_waitingForDownload) {
_session->downloaderTaskFinished(
) | rpl::start_with_next([=] {
check();
}, _downloadCheckLifetime);
} else {
_downloadCheckLifetime.destroy();
_downloadCheckLifetime.destroy();
}
}
void EmojiInteractions::playStarted(not_null<PeerData*> peer, QString emoji) {
auto &map = _playStarted[peer];
const auto i = map.find(emoji);
const auto now = crl::now();
if (i != end(map) && now - i->second < kAccumulateSeenRequests) {
return;
}
_session->api().request(MTPmessages_SetTyping(
MTP_flags(0),
peer->input,
MTPint(), // top_msg_id
MTP_sendMessageEmojiInteractionSeen(MTP_string(emoji))
)).send();
map[emoji] = now;
if (!_checkTimer.isActive()) {
_checkTimer.callOnce(kAccumulateSeenRequests);
}
}
EmojiInteractionsBunch EmojiInteractions::Parse(const QByteArray &json) {
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
const auto document = QJsonDocument::fromJson(json, &error);
if (error.error != QJsonParseError::NoError || !document.isObject()) {
LOG(("API Error: Bad interactions json received."));
return {};
}
const auto root = document.object();
const auto version = root.value("v").toInt();
if (version != kJsonVersion) {
LOG(("API Error: Bad interactions version: %1").arg(version));
return {};
}
const auto actions = root.value("a").toArray();
if (actions.empty()) {
LOG(("API Error: Empty interactions list."));
return {};
}
auto result = EmojiInteractionsBunch();
for (const auto &interaction : actions) {
const auto object = interaction.toObject();
const auto index = object.value("i").toInt();
if (index < 0 || index > 10) {
LOG(("API Error: Bad interaction index: %1").arg(index));
return {};
}
const auto time = object.value("t").toDouble();
if (time < 0.
|| time > 1.
|| (!result.interactions.empty()
&& time <= result.interactions.back().time)) {
LOG(("API Error: Bad interaction time: %1").arg(time));
continue;
}
result.interactions.push_back({ .index = index, .time = time });
}
return result;
}
QByteArray EmojiInteractions::ToJson(const EmojiInteractionsBunch &bunch) {
auto list = QJsonArray();
for (const auto &single : bunch.interactions) {
list.push_back(QJsonObject{
{ "i", single.index },
{ "t", single.time },
});
}
return QJsonDocument(QJsonObject{
{ "v", kJsonVersion },
{ "a", std::move(list) },
}).toJson(QJsonDocument::Compact);
}
} // namespace ChatHelpers

View File

@@ -0,0 +1,147 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/timer.h"
class PeerData;
class HistoryItem;
class DocumentData;
namespace Data {
class DocumentMedia;
} // namespace Data
namespace Main {
class Session;
} // namespace Main
namespace HistoryView {
class Element;
} // namespace HistoryView
namespace ChatHelpers {
struct EmojiInteractionPlayRequest {
QString emoticon;
not_null<HistoryItem*> item;
std::shared_ptr<Data::DocumentMedia> media;
crl::time shouldHaveStartedAt = 0;
bool incoming = false;
};
struct EmojiInteractionsBunch {
struct Single {
int index = 0;
double time = 0;
};
std::vector<Single> interactions;
};
struct EmojiInteractionSeen {
not_null<PeerData*> peer;
QString emoticon;
};
class EmojiInteractions final {
public:
explicit EmojiInteractions(not_null<Main::Session*> session);
~EmojiInteractions();
using PlayRequest = EmojiInteractionPlayRequest;
void startOutgoing(not_null<const HistoryView::Element*> view);
void startIncoming(
not_null<PeerData*> peer,
MsgId messageId,
const QString &emoticon,
EmojiInteractionsBunch &&bunch);
void seenOutgoing(not_null<PeerData*> peer, const QString &emoticon);
[[nodiscard]] rpl::producer<EmojiInteractionSeen> seen() const {
return _seen.events();
}
[[nodiscard]] rpl::producer<PlayRequest> playRequests() const {
return _playRequests.events();
}
void playStarted(not_null<PeerData*> peer, QString emoji);
[[nodiscard]] static EmojiInteractionsBunch Parse(const QByteArray &json);
[[nodiscard]] static QByteArray ToJson(
const EmojiInteractionsBunch &bunch);
private:
struct Animation {
QString emoticon;
not_null<EmojiPtr> emoji;
not_null<DocumentData*> document;
std::shared_ptr<Data::DocumentMedia> media;
crl::time scheduledAt = 0;
crl::time startedAt = 0;
bool incoming = false;
int index = 0;
};
struct PlaySent {
mtpRequestId lastRequestId = 0;
crl::time lastDoneReceivedAt = 0;
};
struct CheckResult {
crl::time nextCheckAt = 0;
bool waitingForDownload = false;
};
[[nodiscard]] static CheckResult Combine(CheckResult a, CheckResult b);
[[nodiscard]] EmojiPtr chooseInteractionEmoji(
not_null<HistoryItem*> item) const;
[[nodiscard]] EmojiPtr chooseInteractionEmoji(
const QString &emoticon) const;
void check(crl::time now = 0);
[[nodiscard]] CheckResult checkAnimations(crl::time now);
[[nodiscard]] CheckResult checkAnimations(
crl::time now,
base::flat_map<not_null<HistoryItem*>, std::vector<Animation>> &map);
[[nodiscard]] CheckResult checkAccumulated(crl::time now);
void sendAccumulatedOutgoing(
crl::time now,
not_null<HistoryItem*> item,
std::vector<Animation> &animations);
void clearAccumulatedIncoming(
crl::time now,
std::vector<Animation> &animations);
void setWaitingForDownload(bool waiting);
void checkSeenRequests(crl::time now);
void checkSentRequests(crl::time now);
void checkEdition(
not_null<HistoryItem*> item,
base::flat_map<not_null<HistoryItem*>, std::vector<Animation>> &map);
const not_null<Main::Session*> _session;
base::flat_map<not_null<HistoryItem*>, std::vector<Animation>> _outgoing;
base::flat_map<not_null<HistoryItem*>, std::vector<Animation>> _incoming;
base::Timer _checkTimer;
rpl::event_stream<PlayRequest> _playRequests;
base::flat_map<
not_null<PeerData*>,
base::flat_map<QString, crl::time>> _playStarted;
base::flat_map<
not_null<PeerData*>,
base::flat_map<not_null<EmojiPtr>, PlaySent>> _playsSent;
rpl::event_stream<EmojiInteractionSeen> _seen;
bool _waitingForDownload = false;
rpl::lifetime _downloadCheckLifetime;
rpl::lifetime _lifetime;
};
} // namespace ChatHelpers

View File

@@ -39,10 +39,10 @@ inline auto PreviewPath(int i) {
}
const auto kSets = {
Set{ {0, 0, 0, "Mac"}, PreviewPath(0) },
Set{ {1, 713, 7'313'166, "Android"}, PreviewPath(1) },
Set{ {2, 714, 4'690'333, "Twemoji"}, PreviewPath(2) },
Set{ {3, 716, 5'968'021, "JoyPixels"}, PreviewPath(3) },
Set{ { 0, 0, 0, "Mac" }, PreviewPath(0) },
Set{ { 1, 1112, 7'914'459, "Android" }, PreviewPath(1) },
Set{ { 2, 1113, 5'287'724, "Twemoji" }, PreviewPath(2) },
Set{ { 3, 1114, 6'687'347, "JoyPixels" }, PreviewPath(3) },
};
using Loading = MTP::DedicatedLoader::Progress;

View File

@@ -35,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/ui_utility.h"
#include "ui/cached_round_corners.h"
#include "base/unixtime.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include "window/window_adaptive.h"
#include "window/window_session_controller.h"
#include "styles/style_chat.h"
@@ -168,11 +168,10 @@ FieldAutocomplete::FieldAutocomplete(
hide();
connect(
_scroll,
&Ui::ScrollArea::geometryChanged,
_inner,
&Inner::onParentGeometryChanged);
_scroll->geometryChanged(
) | rpl::start_with_next(crl::guard(_inner, [=] {
_inner->onParentGeometryChanged();
}), lifetime());
}
not_null<Window::SessionController*> FieldAutocomplete::controller() const {
@@ -667,7 +666,7 @@ void FieldAutocomplete::showAnimated() {
return;
}
if (_cache.isNull()) {
_stickersSeed = openssl::RandomValue<uint64>();
_stickersSeed = base::RandomValue<uint64>();
_scroll->show();
_cache = Ui::GrabWidget(this);
}

View File

@@ -68,7 +68,7 @@ QImage EmojiImageLoader::prepare(EmojiPtr emoji) const {
{ -1, 1 },
{ 1, 1 },
} };
const auto corrected = int(std::round(delta / sqrt(2.)));
const auto corrected = int(base::SafeRound(delta / sqrt(2.)));
for (const auto &shift : diagonal) {
for (auto i = 0; i != corrected; ++i) {
p.drawImage(QPoint(delta, delta) + shift * (i + 1), tinted);

View File

@@ -30,6 +30,18 @@ namespace {
constexpr auto kRefreshTimeout = 7200 * crl::time(1000);
[[nodiscard]] std::optional<int> IndexFromEmoticon(const QString &emoticon) {
if (emoticon.size() < 2) {
return std::nullopt;
}
const auto first = emoticon[0].unicode();
return (first >= '1' && first <= '9')
? std::make_optional(first - '1')
: (first == 55357 && emoticon[1].unicode() == 56607)
? std::make_optional(9)
: std::nullopt;
}
[[nodiscard]] QSize SingleSize() {
const auto single = st::largeEmojiSize;
const auto outline = st::largeEmojiOutline;
@@ -207,6 +219,13 @@ std::shared_ptr<LargeEmojiImage> EmojiPack::image(EmojiPtr emoji) {
return result;
}
auto EmojiPack::animationsForEmoji(EmojiPtr emoji) const
-> const base::flat_map<int, not_null<DocumentData*>> & {
static const auto empty = base::flat_map<int, not_null<DocumentData*>>();
const auto i = _animations.find(emoji);
return (i != end(_animations)) ? i->second : empty;
}
void EmojiPack::refresh() {
if (_requestId) {
return;
@@ -215,7 +234,7 @@ void EmojiPack::refresh() {
MTP_inputStickerSetAnimatedEmoji()
)).done([=](const MTPmessages_StickerSet &result) {
_requestId = 0;
refreshDelayed();
refreshAnimations();
result.match([&](const MTPDmessages_stickerSet &data) {
applySet(data);
});
@@ -225,6 +244,24 @@ void EmojiPack::refresh() {
}).send();
}
void EmojiPack::refreshAnimations() {
if (_animationsRequestId) {
return;
}
_animationsRequestId = _session->api().request(MTPmessages_GetStickerSet(
MTP_inputStickerSetAnimatedEmojiAnimations()
)).done([=](const MTPmessages_StickerSet &result) {
_animationsRequestId = 0;
refreshDelayed();
result.match([&](const MTPDmessages_stickerSet &data) {
applyAnimationsSet(data);
});
}).fail([=](const MTP::Error &error) {
_animationsRequestId = 0;
refreshDelayed();
}).send();
}
void EmojiPack::applySet(const MTPDmessages_stickerSet &data) {
const auto stickers = collectStickers(data.vdocuments().v);
auto was = base::take(_map);
@@ -251,6 +288,55 @@ void EmojiPack::applySet(const MTPDmessages_stickerSet &data) {
}
}
void EmojiPack::applyAnimationsSet(const MTPDmessages_stickerSet &data) {
const auto stickers = collectStickers(data.vdocuments().v);
const auto &packs = data.vpacks().v;
const auto indices = collectAnimationsIndices(packs);
_animations.clear();
for (const auto &pack : packs) {
pack.match([&](const MTPDstickerPack &data) {
const auto emoticon = qs(data.vemoticon());
if (IndexFromEmoticon(emoticon).has_value()) {
return;
}
const auto emoji = Ui::Emoji::Find(emoticon);
if (!emoji) {
return;
}
for (const auto &id : data.vdocuments().v) {
const auto i = indices.find(id.v);
if (i == end(indices)) {
continue;
}
const auto j = stickers.find(id.v);
if (j == end(stickers)) {
continue;
}
for (const auto index : i->second) {
_animations[emoji].emplace(index, j->second);
}
}
});
}
}
auto EmojiPack::collectAnimationsIndices(
const QVector<MTPStickerPack> &packs
) const -> base::flat_map<uint64, base::flat_set<int>> {
auto result = base::flat_map<uint64, base::flat_set<int>>();
for (const auto &pack : packs) {
pack.match([&](const MTPDstickerPack &data) {
if (const auto index = IndexFromEmoticon(qs(data.vemoticon()))) {
for (const auto &id : data.vdocuments().v) {
result[id.v].emplace(*index);
}
}
});
}
return result;
}
void EmojiPack::refreshAll() {
for (const auto &[emoji, list] : _items) {
refreshItems(list);

View File

@@ -67,17 +67,25 @@ public:
[[nodiscard]] Sticker stickerForEmoji(const IsolatedEmoji &emoji);
[[nodiscard]] std::shared_ptr<LargeEmojiImage> image(EmojiPtr emoji);
[[nodiscard]] auto animationsForEmoji(EmojiPtr emoji) const
-> const base::flat_map<int, not_null<DocumentData*>> &;
private:
class ImageLoader;
void refresh();
void refreshDelayed();
void refreshAnimations();
void applySet(const MTPDmessages_stickerSet &data);
void applyPack(
const MTPDstickerPack &data,
const base::flat_map<uint64, not_null<DocumentData*>> &map);
base::flat_map<uint64, not_null<DocumentData*>> collectStickers(
const QVector<MTPDocument> &list) const;
void applyAnimationsSet(const MTPDmessages_stickerSet &data);
[[nodiscard]] auto collectStickers(const QVector<MTPDocument> &list) const
-> base::flat_map<uint64, not_null<DocumentData*>>;
[[nodiscard]] auto collectAnimationsIndices(
const QVector<MTPStickerPack> &packs) const
-> base::flat_map<uint64, base::flat_set<int>>;
void refreshAll();
void refreshItems(EmojiPtr emoji);
void refreshItems(const base::flat_set<not_null<HistoryItem*>> &list);
@@ -90,6 +98,11 @@ private:
base::flat_map<EmojiPtr, std::weak_ptr<LargeEmojiImage>> _images;
mtpRequestId _requestId = 0;
base::flat_map<
EmojiPtr,
base::flat_map<int, not_null<DocumentData*>>> _animations;
mtpRequestId _animationsRequestId = 0;
rpl::lifetime _lifetime;
};

View File

@@ -31,7 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_account.h"
#include "lang/lang_keys.h"
#include "mainwindow.h"
#include "dialogs/dialogs_layout.h"
#include "dialogs/ui/dialogs_layout.h"
#include "boxes/sticker_set_box.h"
#include "boxes/stickers_box.h"
#include "boxes/confirm_box.h"

View File

@@ -44,6 +44,10 @@ enum class StickerLottieSize : uchar {
StickersFooter,
SetsListThumbnail,
InlineResults,
EmojiInteraction,
EmojiInteractionReserved1,
EmojiInteractionReserved2,
EmojiInteractionReserved3,
};
[[nodiscard]] std::unique_ptr<Lottie::SinglePlayer> LottiePlayerFromDocument(

View File

@@ -47,6 +47,55 @@ std::map<int, const char*> BetaLogs() {
"from the context menu.\n"
"- Enable recording with video in live streams and video chats."
},
{
3000004,
"- Fix a crash when joining video chat or live broadcast.\n"
"- Add a \"Close to Taskbar\" option when tray icon is disabled "
"(Windows and Linux)."
},
{
3000005,
"- Add support for Emoji 13.1."
},
{
3001002,
"- Control video in fullscreen mode using arrows and numbers.\n"
"- Open locations in browser if default Bing Maps is not installed.\n"
"- Reconnect without timeout when network availability changes.\n"
"- Crash fixes."
},
{
3001005,
"- Choose one of 8 new preset themes for any individual private chat.\n"
"- Click on '...' menu > 'Change Colors' to pick a theme.\n"
"- Both chat participants will see the same theme in that chat "
" on all their devices.\n"
"- Each new theme features colorful gradient message bubbles, "
"beautifully animated backgrounds and unique background patterns.\n"
"- All chat themes have day and night versions and will follow "
"your overall dark mode settings.\n"
"- Implement main window rounded corners on Windows 11.\n"
"- Fix audio capture from AirPods on macOS.\n"
},
{
3001006,
"- Show small media previews in chats list.\n"
"- Show media album previews and caption text in chats list.\n"
"- Add \"Quick Reply\" and \"Mark as Read\" "
"to native Windows notifications.\n"
}
};
};

View File

@@ -220,7 +220,8 @@ QByteArray Settings::serialize() const {
<< qint32(_disableOpenGL ? 1 : 0)
<< _photoEditorBrush
<< qint32(_groupCallNoiseSuppression ? 1 : 0)
<< qint32(_voicePlaybackSpeed * 100);
<< qint32(_voicePlaybackSpeed * 100)
<< qint32(_closeToTaskbar.current() ? 1 : 0);
}
return result;
}
@@ -303,6 +304,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
QByteArray proxy;
qint32 hiddenGroupCallTooltips = qint32(_hiddenGroupCallTooltips.value());
QByteArray photoEditorBrush = _photoEditorBrush;
qint32 closeToTaskbar = _closeToTaskbar.current() ? 1 : 0;
stream >> themesAccentColors;
if (!stream.atEnd()) {
@@ -460,6 +462,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) {
stream >> voicePlaybackSpeed;
}
if (!stream.atEnd()) {
stream >> closeToTaskbar;
}
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()"));
@@ -600,6 +605,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
: Tooltip(0));
}();
_photoEditorBrush = photoEditorBrush;
_closeToTaskbar = (closeToTaskbar == 1);
}
QString Settings::getSoundPath(const QString &key) const {

View File

@@ -601,10 +601,23 @@ public:
_hiddenGroupCallTooltips |= value;
}
void setCloseToTaskbar(bool value) {
_closeToTaskbar = value;
}
[[nodiscard]] bool closeToTaskbar() const {
return _closeToTaskbar.current();
}
[[nodiscard]] rpl::producer<bool> closeToTaskbarValue() const {
return _closeToTaskbar.value();
}
[[nodiscard]] rpl::producer<bool> closeToTaskbarChanges() const {
return _closeToTaskbar.changes();
}
[[nodiscard]] static bool ThirdColumnByDefault();
[[nodiscard]] static float64 DefaultDialogsWidthRatio();
[[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) {
return int(std::round(std::clamp(speed, 0.5, 2.0) * 100));
return int(base::SafeRound(std::clamp(speed, 0.5, 2.0) * 100));
}
[[nodiscard]] static float64 DeserializePlaybackSpeed(qint32 speed) {
if (speed < 10) {
@@ -700,6 +713,7 @@ private:
bool _disableOpenGL = false;
rpl::variable<WorkMode> _workMode = WorkMode::WindowAndTray;
base::flags<Calls::Group::StickedTooltip> _hiddenGroupCallTooltips;
rpl::variable<bool> _closeToTaskbar = false;
bool _tabbedReplacedWithInfo = false; // per-window
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window

View File

@@ -464,6 +464,7 @@ void Launcher::processArguments() {
{ "-noupdate" , KeyFormat::NoValues },
{ "-tosettings" , KeyFormat::NoValues },
{ "-startintray" , KeyFormat::NoValues },
{ "-quit" , KeyFormat::NoValues },
{ "-sendpath" , KeyFormat::AllLeftValues },
{ "-workdir" , KeyFormat::OneValue },
{ "--" , KeyFormat::OneValue },
@@ -504,6 +505,7 @@ void Launcher::processArguments() {
gNoStartUpdate = parseResult.contains("-noupdate");
gStartToSettings = parseResult.contains("-tosettings");
gStartInTray = parseResult.contains("-startintray");
gQuit = parseResult.contains("-quit");
gSendPaths = parseResult.value("-sendpath", {});
gWorkingDir = parseResult.value("-workdir", {}).join(QString());
if (!gWorkingDir.isEmpty()) {

View File

@@ -35,10 +35,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/player/media_player_instance.h"
#include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "window/themes/window_theme_editor_box.h" // GenerateSlug.
#include "settings/settings_common.h"
#include "mainwidget.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "history/history.h"
#include "apiwrap.h"
#include <QtGui/QGuiApplication>
@@ -307,7 +309,11 @@ bool ResolveUsername(
}
: Navigation::RepliesByLinkInfo{ v::null },
.startToken = startToken,
.voicechatHash = (params.contains(u"voicechat"_q)
.voicechatHash = (params.contains(u"livestream"_q)
? std::make_optional(params.value(u"livestream"_q))
: params.contains(u"videochat"_q)
? std::make_optional(params.value(u"videochat"_q))
: params.contains(u"voicechat"_q)
? std::make_optional(params.value(u"voicechat"_q))
: std::nullopt),
.clickFromMessageId = fromMessageId,
@@ -464,6 +470,126 @@ bool ShowInviteLink(
return true;
}
void ExportTestChatTheme(
not_null<Main::Session*> session,
not_null<const Data::CloudTheme*> theme) {
if (!theme->paper
|| !theme->paper->isPattern()
|| theme->paper->backgroundColors().empty()
|| !theme->accentColor
|| !theme->paper->hasShareUrl()) {
Ui::Toast::Show("Something went wrong :(");
return;
}
const auto &bg = theme->paper->backgroundColors();
const auto url = theme->paper->shareUrl(session);
const auto from = url.indexOf("bg/");
const auto till = url.indexOf("?");
if (from < 0 || till <= from) {
Ui::Toast::Show("Bad WallPaper link: " + url);
return;
}
using Flag = MTPaccount_CreateTheme::Flag;
using Setting = MTPDinputThemeSettings::Flag;
using Paper = MTPDwallPaperSettings::Flag;
const auto color = [](const QColor &color) {
const auto red = color.red();
const auto green = color.green();
const auto blue = color.blue();
return int(((uint32(red) & 0xFFU) << 16)
| ((uint32(green) & 0xFFU) << 8)
| (uint32(blue) & 0xFFU));
};
const auto colors = [&](const std::vector<QColor> &colors) {
auto result = QVector<MTPint>();
result.reserve(colors.size());
for (const auto &single : colors) {
result.push_back(MTP_int(color(single)));
}
return result;
};
const auto slug = url.mid(from + 3, till - from - 3);
const auto flags = Flag::f_settings;
const auto settings = Setting::f_wallpaper
| Setting::f_wallpaper_settings
| (theme->outgoingAccentColor
? Setting::f_outbox_accent_color
: Setting(0))
| (!theme->outgoingMessagesColors.empty()
? Setting::f_message_colors
: Setting(0));
const auto papers = Paper::f_background_color
| Paper::f_intensity
| (bg.size() > 1
? Paper::f_second_background_color
: Paper(0))
| (bg.size() > 2
? Paper::f_third_background_color
: Paper(0))
| (bg.size() > 3
? Paper::f_fourth_background_color
: Paper(0));
session->api().request(MTPaccount_CreateTheme(
MTP_flags(flags),
MTP_string(Window::Theme::GenerateSlug()),
MTP_string(theme->title + " Desktop"),
MTPInputDocument(),
MTP_inputThemeSettings(
MTP_flags(settings),
(theme->basedOnDark
? MTP_baseThemeTinted()
: MTP_baseThemeClassic()),
MTP_int(color(theme->accentColor.value_or(Qt::black))),
MTP_int(color(theme->outgoingAccentColor.value_or(
Qt::black))),
MTP_vector<MTPint>(colors(
theme->outgoingMessagesColors)),
MTP_inputWallPaperSlug(MTP_string(slug)),
MTP_wallPaperSettings(
MTP_flags(papers),
MTP_int(color(bg[0])),
MTP_int(color(bg.size() > 1 ? bg[1] : Qt::black)),
MTP_int(color(bg.size() > 2 ? bg[2] : Qt::black)),
MTP_int(color(bg.size() > 3 ? bg[3] : Qt::black)),
MTP_int(theme->paper->patternIntensity()),
MTP_int(0)))
)).done([=](const MTPTheme &result) {
const auto slug = Data::CloudTheme::Parse(session, result, true).slug;
QGuiApplication::clipboard()->setText(
session->createInternalLinkFull("addtheme/" + slug));
Ui::Toast::Show(tr::lng_background_link_copied(tr::now));
}).fail([=](const MTP::Error &error) {
Ui::Toast::Show("Error: " + error.type());
}).send();
}
bool ResolveTestChatTheme(
Window::SessionController *controller,
const Match &match,
const QVariant &context) {
if (!controller) {
return false;
}
const auto params = url_parse_params(
match->captured(1),
qthelp::UrlParamNameTransform::ToLower);
if (const auto history = controller->activeChatCurrent().history()) {
controller->clearCachedChatThemes();
const auto theme = history->owner().cloudThemes().updateThemeFromLink(
history->peer->themeEmoji(),
params);
if (theme) {
if (!params["export"].isEmpty()) {
ExportTestChatTheme(&controller->session(), &*theme);
}
[[maybe_unused]] auto value = controller->cachedChatThemeValue(
*theme);
}
}
return true;
}
} // namespace
const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
@@ -524,6 +650,10 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
qsl("^settings(/folders|/devices|/language)?$"),
ResolveSettings
},
{
qsl("^test_chat_theme/?\\?(.+)(#|$)"),
ResolveTestChatTheme,
},
{
qsl("^([^\\?]+)(\\?|#|$)"),
HandleUnknown

View File

@@ -237,6 +237,8 @@ void Sandbox::socketConnected() {
}
if (!cStartUrl().isEmpty()) {
commands += qsl("OPEN:") + _escapeTo7bit(cStartUrl()) + ';';
} else if (cQuit()) {
commands += qsl("CMD:quit;");
} else {
commands += qsl("CMD:show;");
}
@@ -305,6 +307,10 @@ void Sandbox::socketError(QLocalSocket::LocalSocketError e) {
return App::quit();
}
if (cQuit()) {
return App::quit();
}
singleInstanceChecked();
}
@@ -605,6 +611,8 @@ void Sandbox::execExternal(const QString &cmd) {
} else if (PreLaunchWindow::instance()) {
PreLaunchWindow::instance()->activate();
}
} else if (cmd == "quit") {
App::quit();
}
}

View File

@@ -586,7 +586,7 @@ bool ParseCommonMap(
}
return string.toULongLong();
} else if ((*version).isDouble()) {
return uint64(std::round((*version).toDouble()));
return uint64(base::SafeRound((*version).toDouble()));
}
return 0ULL;
}();

View File

@@ -371,11 +371,6 @@ char *hashMd5Hex(const int32 *hashmd5, void *dest) {
return md5To;
}
void memset_rand(void *data, uint32 len) {
Assert(_sslInited);
RAND_bytes((uchar*)data, len);
}
namespace {
QMap<QString, QString> fastRusEng;
QHash<QChar, QString> fastLetterRusEng;

View File

@@ -109,9 +109,6 @@ inline std::array<char, 32> hashMd5Hex(const void *data, int size) {
return result;
}
// good random (using openssl implementation)
void memset_rand(void *data, uint32 len);
QString translitRusEng(const QString &rus);
QString rusKeyboardLayoutSwitch(const QString &from);

View File

@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
constexpr auto AppName = "Telegram Desktop"_cs;
constexpr auto AppFile = "Telegram"_cs;
constexpr auto AppVersion = 3000002;
constexpr auto AppVersionStr = "3.0.2";
constexpr auto AppVersion = 3001006;
constexpr auto AppVersionStr = "3.1.6";
constexpr auto AppBetaVersion = true;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -148,7 +148,7 @@ struct MessageUpdate {
NewMaybeAdded = (1U << 7),
RepliesUnreadCount = (1U << 8),
LastUsedBit = (1U << 7),
LastUsedBit = (1U << 8),
};
using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; }

View File

@@ -27,6 +27,8 @@ namespace {
constexpr auto kFirstReloadTimeout = 10 * crl::time(1000);
constexpr auto kReloadTimeout = 3600 * crl::time(1000);
bool IsTestingColors/* = false*/;
} // namespace
CloudTheme CloudTheme::Parse(
@@ -385,30 +387,169 @@ rpl::producer<> CloudThemes::chatThemesUpdated() const {
}
std::optional<ChatTheme> CloudThemes::themeForEmoji(
const QString &emoji) const {
if (emoji.isEmpty()) {
const QString &emoticon) const {
const auto emoji = Ui::Emoji::Find(emoticon);
if (!emoji) {
return {};
}
const auto i = ranges::find(_chatThemes, emoji, &ChatTheme::emoji);
const auto i = ranges::find(_chatThemes, emoji, [](const ChatTheme &v) {
return Ui::Emoji::Find(v.emoticon);
});
return (i != end(_chatThemes)) ? std::make_optional(*i) : std::nullopt;
}
rpl::producer<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue(
const QString &emoji) {
if (emoji.isEmpty()) {
const QString &emoticon) {
const auto testing = TestingColors();
if (!Ui::Emoji::Find(emoticon)) {
return rpl::single<std::optional<ChatTheme>>(std::nullopt);
} else if (auto result = themeForEmoji(emoji)) {
} else if (auto result = themeForEmoji(emoticon)) {
if (testing) {
return rpl::single(
std::move(result)
) | rpl::then(chatThemesUpdated(
) | rpl::map([=] {
return themeForEmoji(emoticon);
}) | rpl::filter([](const std::optional<ChatTheme> &theme) {
return theme.has_value();
}));
}
return rpl::single(std::move(result));
}
refreshChatThemes();
const auto limit = testing ? (1 << 20) : 1;
return rpl::single<std::optional<ChatTheme>>(
std::nullopt
) | rpl::then(chatThemesUpdated(
) | rpl::map([=] {
return themeForEmoji(emoji);
return themeForEmoji(emoticon);
}) | rpl::filter([](const std::optional<ChatTheme> &theme) {
return theme.has_value();
}) | rpl::take(1));
}) | rpl::take(limit));
}
bool CloudThemes::TestingColors() {
return IsTestingColors;
}
void CloudThemes::SetTestingColors(bool testing) {
IsTestingColors = testing;
}
QString CloudThemes::prepareTestingLink(const CloudTheme &theme) const {
const auto hex = [](int value) {
return QChar((value < 10) ? ('0' + value) : ('a' + (value - 10)));
};
const auto hex2 = [&](int value) {
return QString() + hex(value / 16) + hex(value % 16);
};
const auto color = [&](const QColor &color) {
return hex2(color.red()) + hex2(color.green()) + hex2(color.blue());
};
const auto colors = [&](const std::vector<QColor> &colors) {
auto list = QStringList();
for (const auto &c : colors) {
list.push_back(color(c));
}
return list.join(",");
};
auto arguments = QStringList();
if (theme.basedOnDark) {
arguments.push_back("dark=1");
}
if (theme.accentColor) {
arguments.push_back("accent=" + color(*theme.accentColor));
}
if (theme.paper && !theme.paper->backgroundColors().empty()) {
arguments.push_back("bg=" + colors(theme.paper->backgroundColors()));
}
if (theme.paper/* && theme.paper->hasShareUrl()*/) {
arguments.push_back("intensity="
+ QString::number(theme.paper->patternIntensity()));
//const auto url = theme.paper->shareUrl(_session);
//const auto from = url.indexOf("bg/");
//const auto till = url.indexOf("?");
//if (from > 0 && till > from) {
// arguments.push_back("slug=" + url.mid(from + 3, till - from - 3));
//}
}
if (theme.outgoingAccentColor) {
arguments.push_back("out_accent" + color(*theme.outgoingAccentColor));
}
if (!theme.outgoingMessagesColors.empty()) {
arguments.push_back("out_bg=" + colors(theme.outgoingMessagesColors));
}
return arguments.isEmpty()
? QString()
: ("tg://test_chat_theme?" + arguments.join("&"));
}
std::optional<CloudTheme> CloudThemes::updateThemeFromLink(
const QString &emoticon,
const QMap<QString, QString> &params) {
const auto emoji = Ui::Emoji::Find(emoticon);
if (!TestingColors() || !emoji) {
return std::nullopt;
}
const auto i = ranges::find(_chatThemes, emoji, [](const ChatTheme &v) {
return Ui::Emoji::Find(v.emoticon);
});
if (i == end(_chatThemes)) {
return std::nullopt;
}
const auto hex = [](const QString &value) {
return (value.size() != 1)
? std::nullopt
: (value[0] >= 'a' && value[0] <= 'f')
? std::make_optional(10 + int(value[0].unicode() - 'a'))
: (value[0] >= 'A' && value[0] <= 'F')
? std::make_optional(10 + int(value[0].unicode() - 'A'))
: (value[0] >= '0' && value[0] <= '9')
? std::make_optional(int(value[0].unicode() - '0'))
: std::nullopt;
};
const auto hex2 = [&](const QString &value) {
const auto first = hex(value.mid(0, 1));
const auto second = hex(value.mid(1, 1));
return (first && second)
? std::make_optional((*first) * 16 + (*second))
: std::nullopt;
};
const auto color = [&](const QString &value) {
const auto red = hex2(value.mid(0, 2));
const auto green = hex2(value.mid(2, 2));
const auto blue = hex2(value.mid(4, 2));
return (red && green && blue)
? std::make_optional(QColor(*red, *green, *blue))
: std::nullopt;
};
const auto colors = [&](const QString &value) {
auto list = value.split(",");
auto result = std::vector<QColor>();
for (const auto &single : list) {
if (const auto c = color(single)) {
result.push_back(*c);
} else {
return std::vector<QColor>();
}
}
return (result.size() > 4) ? std::vector<QColor>() : result;
};
auto &applyTo = params["dark"].isEmpty() ? i->light : i->dark;
applyTo.accentColor = color(params["accent"]);
const auto bg = colors(params["bg"]);
applyTo.paper = (applyTo.paper && !bg.empty())
? std::make_optional(applyTo.paper->withBackgroundColors(bg))
: applyTo.paper;
applyTo.paper = (applyTo.paper && params["intensity"].toInt())
? std::make_optional(
applyTo.paper->withPatternIntensity(params["intensity"].toInt()))
: applyTo.paper;
applyTo.outgoingAccentColor = color(params["out_accent"]);
applyTo.outgoingMessagesColors = colors(params["out_bg"]);
_chatThemesUpdates.fire({});
return applyTo;
}
void CloudThemes::parseChatThemes(const QVector<MTPChatTheme> &list) {
@@ -417,7 +558,7 @@ void CloudThemes::parseChatThemes(const QVector<MTPChatTheme> &list) {
for (const auto &theme : list) {
theme.match([&](const MTPDchatTheme &data) {
_chatThemes.push_back({
.emoji = qs(data.vemoticon()),
.emoticon = qs(data.vemoticon()),
.light = CloudTheme::Parse(_session, data.vtheme(), true),
.dark = CloudTheme::Parse(_session, data.vdark_theme(), true),
});

View File

@@ -50,7 +50,7 @@ struct CloudTheme {
};
struct ChatTheme {
QString emoji;
QString emoticon;
CloudTheme light;
CloudTheme dark;
};
@@ -71,9 +71,16 @@ public:
[[nodiscard]] const std::vector<ChatTheme> &chatThemes() const;
[[nodiscard]] rpl::producer<> chatThemesUpdated() const;
[[nodiscard]] std::optional<ChatTheme> themeForEmoji(
const QString &emoji) const;
const QString &emoticon) const;
[[nodiscard]] rpl::producer<std::optional<ChatTheme>> themeForEmojiValue(
const QString &emoji);
const QString &emoticon);
[[nodiscard]] static bool TestingColors();
static void SetTestingColors(bool testing);
[[nodiscard]] QString prepareTestingLink(const CloudTheme &theme) const;
[[nodiscard]] std::optional<CloudTheme> updateThemeFromLink(
const QString &emoticon,
const QMap<QString, QString> &params);
void applyUpdate(const MTPTheme &theme);

View File

@@ -19,6 +19,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data {
DraftKey DraftKey::FromSerializedOld(int32 value) {
return !value
? DraftKey::None()
: (value == kLocalDraftIndex + kEditDraftShiftOld)
? DraftKey::LocalEdit()
: (value == kScheduledDraftIndex + kEditDraftShiftOld)
? DraftKey::ScheduledEdit()
: (value > 0 && value < 0x4000'0000)
? DraftKey::Replies(int64(value))
: (value > kEditDraftShiftOld
&& value < kEditDraftShiftOld + 0x4000'000)
? DraftKey::RepliesEdit(int64(value - kEditDraftShiftOld))
: DraftKey::None();
}
Draft::Draft(
const TextWithTags &textWithTags,
MsgId msgId,

View File

@@ -75,19 +75,21 @@ public:
return kScheduledDraftIndex + kEditDraftShift;
}
[[nodiscard]] static DraftKey Replies(MsgId rootId) {
return rootId;
return rootId.bare;
}
[[nodiscard]] static DraftKey RepliesEdit(MsgId rootId) {
return rootId + kEditDraftShift;
return rootId.bare + kEditDraftShift;
}
[[nodiscard]] static DraftKey FromSerialized(int32 value) {
[[nodiscard]] static DraftKey FromSerialized(qint64 value) {
return value;
}
[[nodiscard]] int32 serialize() const {
[[nodiscard]] qint64 serialize() const {
return _value;
}
[[nodiscard]] static DraftKey FromSerializedOld(int32 value);
inline bool operator<(const DraftKey &other) const {
return _value < other._value;
}
@@ -111,15 +113,16 @@ public:
}
private:
DraftKey(int value) : _value(value) {
DraftKey(int64 value) : _value(value) {
}
static constexpr auto kLocalDraftIndex = -1;
static constexpr auto kCloudDraftIndex = -2;
static constexpr auto kScheduledDraftIndex = -3;
static constexpr auto kEditDraftShift = ServerMaxMsgId;
static constexpr auto kEditDraftShift = ServerMaxMsgId.bare;
static constexpr auto kEditDraftShiftOld = 0x3FFF'FFFF;
int _value = 0;
int64 _value = 0;
};

View File

@@ -7,7 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_groups.h"
#include "history/history.h"
#include "history/history_item.h"
#include "dialogs/ui/dialogs_message_view.h"
#include "data/data_media_types.h"
#include "data/data_session.h"
@@ -140,8 +142,13 @@ const Group *Groups::find(not_null<const HistoryItem*> item) const {
}
void Groups::refreshViews(const HistoryItemsList &items) {
if (items.empty()) {
return;
}
const auto history = items.front()->history();
for (const auto &item : items) {
_data->requestItemViewRefresh(item);
history->lastItemDialogsView.itemInvalidated(item);
}
}

View File

@@ -15,7 +15,6 @@ class Session;
struct Group {
HistoryItemsList items;
};
class Groups {

View File

@@ -72,13 +72,13 @@ void Histories::readInbox(not_null<History*> history) {
if (history->lastServerMessageKnown()) {
const auto last = history->lastServerMessage();
DEBUG_LOG(("Reading: last known, reading till %1."
).arg(last ? last->id : 0));
).arg(last ? last->id.bare : 0));
readInboxTill(history, last ? last->id : 0);
return;
} else if (history->loadedAtBottom()) {
if (const auto lastId = history->maxMsgId()) {
DEBUG_LOG(("Reading: loaded at bottom, maxMsgId %1."
).arg(lastId));
).arg(lastId.bare));
readInboxTill(history, lastId);
return;
} else if (history->loadedAtTop()) {
@@ -93,7 +93,7 @@ void Histories::readInbox(not_null<History*> history) {
const auto last = history->lastServerMessage();
DEBUG_LOG(("Reading: got entry, reading till %1."
).arg(last ? last->id : 0));
).arg(last ? last->id.bare : 0));
readInboxTill(history, last ? last->id : 0);
});
}
@@ -147,7 +147,7 @@ void Histories::readInboxTill(
Expects(IsServerMsgId(tillId) || (!tillId && !force));
DEBUG_LOG(("Reading: readInboxTill %1, force %2."
).arg(tillId
).arg(tillId.bare
).arg(Logs::b(force)));
const auto syncGuard = gsl::finally([&] {
@@ -156,8 +156,8 @@ void Histories::readInboxTill(
if (history->unreadCount() > 0) {
if (const auto last = history->lastServerMessage()) {
DEBUG_LOG(("Reading: checking last %1 and %2."
).arg(last->id
).arg(tillId));
).arg(last->id.bare
).arg(tillId.bare));
if (last->id == tillId) {
DEBUG_LOG(("Reading: locally marked as read."));
history->setUnreadCount(0);
@@ -180,11 +180,11 @@ void Histories::readInboxTill(
const auto maybeState = lookup(history);
if (maybeState && maybeState->sentReadTill >= tillId) {
DEBUG_LOG(("Reading: readInboxTill finish 3 with %1."
).arg(maybeState->sentReadTill));
).arg(maybeState->sentReadTill.bare));
return;
} else if (maybeState && maybeState->willReadTill >= tillId) {
DEBUG_LOG(("Reading: readInboxTill finish 4 with %1 and force %2."
).arg(maybeState->sentReadTill
).arg(maybeState->sentReadTill.bare
).arg(Logs::b(force)));
if (force) {
sendPendingReadInbox(history);
@@ -200,7 +200,7 @@ void Histories::readInboxTill(
&& history->unreadCountKnown()
&& *stillUnread == history->unreadCount()) {
DEBUG_LOG(("Reading: count didn't change so just update till %1"
).arg(tillId));
).arg(tillId.bare));
history->setInboxReadTill(tillId);
return;
}
@@ -208,7 +208,7 @@ void Histories::readInboxTill(
state.willReadTill = tillId;
if (force || !stillUnread || !*stillUnread) {
DEBUG_LOG(("Reading: will read till %1 with still unread %2"
).arg(tillId
).arg(tillId.bare
).arg(stillUnread.value_or(-666)));
state.willReadWhen = 0;
sendReadRequests();
@@ -216,17 +216,18 @@ void Histories::readInboxTill(
return;
}
} else if (!state.willReadWhen) {
DEBUG_LOG(("Reading: will read till %1 with postponed").arg(tillId));
DEBUG_LOG(("Reading: will read till %1 with postponed"
).arg(tillId.bare));
state.willReadWhen = crl::now() + kReadRequestTimeout;
if (!_readRequestsTimer.isActive()) {
_readRequestsTimer.callOnce(kReadRequestTimeout);
}
} else {
DEBUG_LOG(("Reading: will read till %1 postponed already"
).arg(tillId));
).arg(tillId.bare));
}
DEBUG_LOG(("Reading: marking now with till %1 and still %2"
).arg(tillId
).arg(tillId.bare
).arg(*stillUnread));
history->setInboxReadTill(tillId);
history->setUnreadCount(*stillUnread);
@@ -437,10 +438,53 @@ void Histories::requestFakeChatListMessage(
});
}
void Histories::requestGroupAround(not_null<HistoryItem*> item) {
const auto history = item->history();
const auto id = item->id;
const auto i = _chatListGroupRequests.find(history);
if (i != end(_chatListGroupRequests)) {
if (i->second.aroundId == id) {
return;
} else {
cancelRequest(i->second.requestId);
_chatListGroupRequests.erase(i);
}
}
constexpr auto kMaxAlbumCount = 10;
const auto requestId = sendRequest(history, RequestType::History, [=](
Fn<void()> finish) {
return session().api().request(MTPmessages_GetHistory(
history->peer->input,
MTP_int(id),
MTP_int(0), // offset_date
MTP_int(-kMaxAlbumCount),
MTP_int(2 * kMaxAlbumCount - 1),
MTP_int(0), // max_id
MTP_int(0), // min_id
MTP_long(0) // hash
)).done([=](const MTPmessages_Messages &result) {
_owner->processExistingMessages(
history->peer->asChannel(),
result);
_chatListGroupRequests.remove(history);
history->migrateToOrMe()->applyChatListGroup(
history->channelId(),
result);
finish();
}).fail([=](const MTP::Error &error) {
_chatListGroupRequests.remove(history);
finish();
}).send();
});
_chatListGroupRequests.emplace(
history,
ChatListGroupRequest{ .aroundId = id, .requestId = requestId });
}
void Histories::sendPendingReadInbox(not_null<History*> history) {
if (const auto state = lookup(history)) {
DEBUG_LOG(("Reading: send pending now with till %1 and when %2"
).arg(state->willReadTill
).arg(state->willReadTill.bare
).arg(state->willReadWhen));
if (state->willReadTill && state->willReadWhen) {
state->willReadWhen = 0;
@@ -462,7 +506,7 @@ void Histories::sendReadRequests() {
continue;
} else if (state.willReadWhen <= now) {
DEBUG_LOG(("Reading: sending with till %1."
).arg(state.willReadTill));
).arg(state.willReadTill.bare));
sendReadRequest(history, state);
} else if (!next || *next > state.willReadWhen) {
DEBUG_LOG(("Reading: scheduling for later send."));
@@ -483,10 +527,10 @@ void Histories::sendReadRequest(not_null<History*> history, State &state) {
state.willReadWhen = 0;
state.sentReadDone = false;
DEBUG_LOG(("Reading: sending request now with till %1."
).arg(tillId));
).arg(tillId.bare));
sendRequest(history, RequestType::ReadInbox, [=](Fn<void()> finish) {
DEBUG_LOG(("Reading: sending request invoked with till %1."
).arg(tillId));
).arg(tillId.bare));
const auto finished = [=] {
const auto state = lookup(history);
Assert(state != nullptr);

View File

@@ -59,6 +59,8 @@ public:
void changeDialogUnreadMark(not_null<History*> history, bool unread);
void requestFakeChatListMessage(not_null<History*> history);
void requestGroupAround(not_null<HistoryItem*> item);
void deleteMessages(
not_null<History*> history,
const QVector<MTPint> &ids,
@@ -95,6 +97,10 @@ private:
bool sentReadDone = false;
bool postponedRequestEntry = false;
};
struct ChatListGroupRequest {
MsgId aroundId = 0;
mtpRequestId requestId = 0;
};
void readInboxTill(not_null<History*> history, MsgId tillId, bool force);
void sendReadRequests();
@@ -130,6 +136,10 @@ private:
base::flat_set<not_null<History*>> _fakeChatListRequests;
base::flat_map<
not_null<History*>,
ChatListGroupRequest> _chatListGroupRequests;
};
} // namespace Data

View File

@@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_theme_document.h"
#include "history/view/media/history_view_slot_machine.h"
#include "history/view/media/history_view_dice.h"
#include "dialogs/ui/dialogs_message_view.h"
#include "ui/image/image.h"
#include "ui/text/format_song_document_name.h"
#include "ui/text/format_values.h"
@@ -37,23 +38,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localstorage.h"
#include "chat_helpers/stickers_dice_pack.h" // Stickers::DicePacks::IsSlot.
#include "data/data_session.h"
#include "data/data_auto_download.h"
#include "data/data_photo.h"
#include "data/data_photo_media.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_game.h"
#include "data/data_web_page.h"
#include "data/data_poll.h"
#include "data/data_channel.h"
#include "data/data_file_origin.h"
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "lang/lang_keys.h"
#include "storage/file_upload.h"
#include "app.h"
#include "styles/style_chat.h"
#include "styles/style_dialogs.h"
namespace Data {
namespace {
constexpr auto kFastRevokeRestriction = 24 * 60 * TimeId(60);
constexpr auto kMaxPreviewImages = 3;
using ItemPreview = HistoryView::ItemPreview;
using ItemPreviewImage = HistoryView::ItemPreviewImage;
[[nodiscard]] Call ComputeCallData(const MTPDmessageActionPhoneCall &call) {
auto result = Call();
@@ -98,20 +108,23 @@ constexpr auto kFastRevokeRestriction = 24 * 60 * TimeId(60);
[[nodiscard]] QString WithCaptionDialogsText(
const QString &attachType,
const QString &caption) {
const QString &caption,
bool hasMiniImages) {
if (caption.isEmpty()) {
return textcmdLink(1, TextUtilities::Clean(attachType));
}
return tr::lng_dialogs_text_media(
tr::now,
lt_media_part,
textcmdLink(1, tr::lng_dialogs_text_media_wrapped(
return hasMiniImages
? TextUtilities::Clean(caption)
: tr::lng_dialogs_text_media(
tr::now,
lt_media,
TextUtilities::Clean(attachType))),
lt_caption,
TextUtilities::Clean(caption));
lt_media_part,
textcmdLink(1, tr::lng_dialogs_text_media_wrapped(
tr::now,
lt_media,
TextUtilities::Clean(attachType))),
lt_caption,
TextUtilities::Clean(caption));
}
[[nodiscard]] QString WithCaptionNotificationText(
@@ -132,6 +145,151 @@ constexpr auto kFastRevokeRestriction = 24 * 60 * TimeId(60);
caption);
}
[[nodiscard]] QImage PreparePreviewImage(
not_null<const Image*> image,
ImageRoundRadius radius = ImageRoundRadius::Small) {
const auto original = image->original();
if (original.width() * 10 < original.height()
|| original.height() * 10 < original.width()) {
return QImage();
}
const auto factor = style::DevicePixelRatio();
const auto size = st::dialogsMiniPreview * factor;
const auto scaled = original.scaled(
QSize(size, size),
Qt::KeepAspectRatioByExpanding,
Qt::SmoothTransformation);
auto square = scaled.copy(
(scaled.width() - size) / 2,
(scaled.height() - size) / 2,
size,
size
).convertToFormat(QImage::Format_ARGB32_Premultiplied);
if (radius == ImageRoundRadius::Small) {
struct Cache {
base::flat_map<int, std::array<QImage, 4>> all;
std::array<QImage, 4> *lastUsed = nullptr;
int lastUsedRadius = 0;
};
static auto cache = Cache();
const auto pxRadius = st::dialogsMiniPreviewRadius;
if (!cache.lastUsed || cache.lastUsedRadius != pxRadius) {
cache.lastUsedRadius = pxRadius;
const auto i = cache.all.find(pxRadius);
if (i != end(cache.all)) {
cache.lastUsed = &i->second;
} else {
cache.lastUsed = &cache.all.emplace(
pxRadius,
Images::CornersMask(pxRadius)).first->second;
}
}
Images::prepareRound(square, *cache.lastUsed);
} else {
Images::prepareRound(square, radius);
}
square.setDevicePixelRatio(factor);
return square;
}
[[nodiscard]] ItemPreviewImage PreparePhotoPreview(
not_null<const HistoryItem*> item,
const std::shared_ptr<PhotoMedia> &media,
ImageRoundRadius radius) {
const auto photo = media->owner();
const auto readyCacheKey = reinterpret_cast<uint64>(photo.get());
if (const auto small = media->image(PhotoSize::Small)) {
return { PreparePreviewImage(small, radius), readyCacheKey };
} else if (const auto thumbnail = media->image(PhotoSize::Thumbnail)) {
return { PreparePreviewImage(thumbnail, radius), readyCacheKey };
} else if (const auto large = media->image(PhotoSize::Large)) {
return { PreparePreviewImage(large, radius), readyCacheKey };
}
const auto allowedToDownload = [&] {
const auto photo = media->owner();
if (media->loaded() || photo->cancelled()) {
return false;
}
return photo->hasExact(PhotoSize::Small)
|| photo->hasExact(PhotoSize::Thumbnail)
|| AutoDownload::Should(
photo->session().settings().autoDownload(),
item->history()->peer,
photo);
}();
const auto cacheKey = allowedToDownload ? 0 : readyCacheKey;
if (allowedToDownload) {
media->owner()->load(PhotoSize::Small, item->fullId());
}
if (const auto blurred = media->thumbnailInline()) {
return { PreparePreviewImage(blurred, radius), cacheKey };
}
return { QImage(), allowedToDownload ? 0 : cacheKey };
}
[[nodiscard]] ItemPreviewImage PrepareFilePreviewImage(
not_null<const HistoryItem*> item,
const std::shared_ptr<DocumentMedia> &media,
ImageRoundRadius radius) {
Expects(media->owner()->hasThumbnail());
const auto document = media->owner();
const auto readyCacheKey = reinterpret_cast<uint64>(document.get());
if (const auto thumbnail = media->thumbnail()) {
return { PreparePreviewImage(thumbnail, radius), readyCacheKey };
}
document->loadThumbnail(item->fullId());
if (const auto blurred = media->thumbnailInline()) {
return { PreparePreviewImage(blurred, radius), 0 };
}
return { QImage(), 0 };
}
[[nodiscard]] QImage PutPlayIcon(QImage preview) {
Expects(!preview.isNull());
{
QPainter p(&preview);
st::dialogsMiniPlay.paintInCenter(
p,
QRect(QPoint(), preview.size() / preview.devicePixelRatio()));
}
return preview;
}
[[nodiscard]] ItemPreviewImage PrepareFilePreview(
not_null<const HistoryItem*> item,
const std::shared_ptr<DocumentMedia> &media,
ImageRoundRadius radius) {
auto result = PrepareFilePreviewImage(item, media, radius);
const auto document = media->owner();
if (!result.data.isNull()
&& (document->isVideoFile() || document->isVideoMessage())) {
result.data = PutPlayIcon(std::move(result.data));
}
return result;
}
[[nodiscard]] bool TryFilePreview(not_null<DocumentData*> document) {
return document->hasThumbnail()
&& !document->sticker()
&& !document->isAudioFile();
}
template <typename MediaType>
[[nodiscard]] ItemPreviewImage FindCachedPreview(
const std::vector<ItemPreviewImage> *existing,
not_null<MediaType*> data) {
if (!existing) {
return {};
}
const auto i = ranges::find(
*existing,
reinterpret_cast<uint64>(data.get()),
&ItemPreviewImage::cacheKey);
return (i != end(*existing)) ? *i : ItemPreviewImage();
}
} // namespace
TextForMimeData WithCaptionClipboardText(
@@ -201,11 +359,12 @@ bool Media::canBeGrouped() const {
return false;
}
QString Media::chatListText(DrawInDialog way) const {
ItemPreview Media::toPreview(ToPreviewOptions options) const {
auto result = notificationText();
return result.isEmpty()
auto text = result.isEmpty()
? QString()
: textcmdLink(1, TextUtilities::Clean(std::move(result)));
return { .text = std::move(text) };
}
bool Media::hasReplyPreview() const {
@@ -270,6 +429,52 @@ std::unique_ptr<HistoryView::Media> Media::createView(
return createView(message, message->data(), replacing);
}
ItemPreview Media::toGroupPreview(
const HistoryItemsList &items,
ToPreviewOptions options) const {
const auto genericText = textcmdLink(
1,
TextUtilities::Clean(tr::lng_in_dlg_album(tr::now)));
auto result = ItemPreview();
auto loadingContext = std::vector<std::any>();
for (const auto &item : items) {
if (const auto media = item->media()) {
auto copy = options;
copy.ignoreGroup = true;
const auto already = int(result.images.size());
const auto left = kMaxPreviewImages - already;
auto single = left ? media->toPreview(copy) : ItemPreview();
if (!single.images.empty()) {
while (single.images.size() > left) {
single.images.pop_back();
}
result.images.insert(
end(result.images),
std::make_move_iterator(begin(single.images)),
std::make_move_iterator(end(single.images)));
}
if (single.loadingContext.has_value()) {
loadingContext.push_back(std::move(single.loadingContext));
}
const auto original = item->originalText().text;
if (!original.isEmpty()) {
if (result.text.isEmpty()) {
result.text = TextUtilities::Clean(original);
} else {
result.text = genericText;
}
}
}
}
if (result.text.isEmpty()) {
result.text = genericText;
}
if (!loadingContext.empty()) {
result.loadingContext = std::move(loadingContext);
}
return result;
}
MediaPhoto::MediaPhoto(
not_null<HistoryItem*> parent,
not_null<PhotoData*> photo)
@@ -341,11 +546,40 @@ QString MediaPhoto::notificationText() const {
parent()->originalText().text);
}
QString MediaPhoto::chatListText(DrawInDialog way) const {
const auto caption = (way == DrawInDialog::WithoutSenderAndCaption)
ItemPreview MediaPhoto::toPreview(ToPreviewOptions options) const {
const auto item = parent();
if (!options.ignoreGroup && item->groupId()) {
if (const auto group = item->history()->owner().groups().find(item)
; group && group->items.size() > 1) {
return toGroupPreview(group->items, options);
}
}
auto images = std::vector<ItemPreviewImage>();
auto context = std::any();
if (auto cached = FindCachedPreview(options.existing, _photo)) {
images.push_back(std::move(cached));
} else {
const auto media = _photo->createMediaView();
const auto radius = _chat
? ImageRoundRadius::Ellipse
: ImageRoundRadius::Small;
if (auto prepared = PreparePhotoPreview(parent(), media, radius)
; prepared || !prepared.cacheKey) {
images.push_back(std::move(prepared));
if (!prepared.cacheKey) {
context = media;
}
}
}
const auto type = tr::lng_in_dlg_photo(tr::now);
const auto caption = options.hideCaption
? QString()
: parent()->originalText().text;
return WithCaptionDialogsText(tr::lng_in_dlg_photo(tr::now), caption);
return {
.text = WithCaptionDialogsText(type, caption, !images.empty()),
.images = std::move(images),
.loadingContext = std::move(context),
};
}
QString MediaPhoto::pinnedTextSubstring() const {
@@ -511,16 +745,40 @@ bool MediaFile::replyPreviewLoaded() const {
return _document->replyPreviewLoaded();
}
QString MediaFile::chatListText(DrawInDialog way) const {
ItemPreview MediaFile::toPreview(ToPreviewOptions options) const {
const auto item = parent();
if (!options.ignoreGroup && item->groupId()) {
if (const auto group = item->history()->owner().groups().find(item)
; group && group->items.size() > 1) {
return toGroupPreview(group->items, options);
}
}
if (const auto sticker = _document->sticker()) {
return Media::chatListText(way);
return Media::toPreview(options);
}
auto images = std::vector<ItemPreviewImage>();
auto context = std::any();
if (auto cached = FindCachedPreview(options.existing, _document)) {
images.push_back(std::move(cached));
} else if (TryFilePreview(_document)) {
const auto media = _document->createMediaView();
const auto radius = _document->isVideoMessage()
? ImageRoundRadius::Ellipse
: ImageRoundRadius::Small;
if (auto prepared = PrepareFilePreview(parent(), media, radius)
; prepared || !prepared.cacheKey) {
images.push_back(std::move(prepared));
if (!prepared.cacheKey) {
context = media;
}
}
}
const auto type = [&] {
using namespace Ui::Text;
if (_document->isVideoMessage()) {
return tr::lng_in_dlg_video_message(tr::now);
} else if (_document->isAnimation()) {
return qsl("GIF");
return u"GIF"_q;
} else if (_document->isVideoFile()) {
return tr::lng_in_dlg_video(tr::now);
} else if (_document->isVoiceMessage()) {
@@ -533,10 +791,14 @@ QString MediaFile::chatListText(DrawInDialog way) const {
}
return tr::lng_in_dlg_file(tr::now);
}();
const auto caption = (way == DrawInDialog::WithoutSenderAndCaption)
const auto caption = options.hideCaption
? QString()
: parent()->originalText().text;
return WithCaptionDialogsText(type, caption);
return {
.text = WithCaptionDialogsText(type, caption, !images.empty()),
.images = std::move(images),
.loadingContext = std::move(context),
};
}
QString MediaFile::notificationText() const {
@@ -855,8 +1117,10 @@ Data::CloudImage *MediaLocation::location() const {
return _location;
}
QString MediaLocation::chatListText(DrawInDialog way) const {
return WithCaptionDialogsText(tr::lng_maps_point(tr::now), _title);
ItemPreview MediaLocation::toPreview(ToPreviewOptions options) const {
const auto type = tr::lng_maps_point(tr::now);
const auto hasMiniImages = false;
return { .text = WithCaptionDialogsText(type, _title, hasMiniImages) };
}
QString MediaLocation::notificationText() const {
@@ -1049,8 +1313,8 @@ bool MediaWebPage::replyPreviewLoaded() const {
return true;
}
QString MediaWebPage::chatListText(DrawInDialog way) const {
return notificationText();
ItemPreview MediaWebPage::toPreview(ToPreviewOptions options) const {
return { .text = notificationText() };
}
QString MediaWebPage::notificationText() const {

View File

@@ -27,7 +27,9 @@ namespace HistoryView {
enum class Context : char;
class Element;
class Media;
enum class DrawInDialog;
struct ItemPreview;
struct ItemPreviewImage;
struct ToPreviewOptions;
} // namespace HistoryView
namespace Data {
@@ -73,7 +75,9 @@ public:
not_null<HistoryItem*> parent() const;
using DrawInDialog = HistoryView::DrawInDialog;
using ToPreviewOptions = HistoryView::ToPreviewOptions;
using ItemPreviewImage = HistoryView::ItemPreviewImage;
using ItemPreview = HistoryView::ItemPreview;
virtual std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) = 0;
@@ -95,7 +99,7 @@ public:
virtual bool replyPreviewLoaded() const;
// Returns text with link-start and link-end commands for service-color highlighting.
// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
virtual QString chatListText(DrawInDialog way) const;
virtual ItemPreview toPreview(ToPreviewOptions way) const;
virtual QString notificationText() const = 0;
virtual QString pinnedTextSubstring() const = 0;
virtual TextForMimeData clipboardText() const = 0;
@@ -125,6 +129,11 @@ public:
not_null<HistoryView::Element*> message,
HistoryView::Element *replacing = nullptr);
protected:
[[nodiscard]] ItemPreview toGroupPreview(
const HistoryItemsList &items,
ToPreviewOptions options) const;
private:
const not_null<HistoryItem*> _parent;
@@ -151,7 +160,7 @@ public:
bool hasReplyPreview() const override;
Image *replyPreview() const override;
bool replyPreviewLoaded() const override;
QString chatListText(DrawInDialog way) const override;
ItemPreview toPreview(ToPreviewOptions options) const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextForMimeData clipboardText() const override;
@@ -189,7 +198,7 @@ public:
bool hasReplyPreview() const override;
Image *replyPreview() const override;
bool replyPreviewLoaded() const override;
QString chatListText(DrawInDialog way) const override;
ItemPreview toPreview(ToPreviewOptions options) const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextForMimeData clipboardText() const override;
@@ -255,7 +264,7 @@ public:
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
Data::CloudImage *location() const override;
QString chatListText(DrawInDialog way) const override;
ItemPreview toPreview(ToPreviewOptions options) const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextForMimeData clipboardText() const override;
@@ -323,7 +332,7 @@ public:
bool hasReplyPreview() const override;
Image *replyPreview() const override;
bool replyPreviewLoaded() const override;
QString chatListText(DrawInDialog way) const override;
ItemPreview toPreview(ToPreviewOptions options) const override;
QString notificationText() const override;
QString pinnedTextSubstring() const override;
TextForMimeData clipboardText() const override;

View File

@@ -0,0 +1,192 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_peer_id.h"
struct MsgId {
constexpr MsgId() noexcept = default;
constexpr MsgId(int64 value) noexcept : bare(value) {
}
[[nodiscard]] constexpr explicit operator bool() const noexcept {
return (bare != 0);
}
[[nodiscard]] constexpr bool operator!() const noexcept {
return !bare;
}
[[nodiscard]] constexpr MsgId operator-() const noexcept {
return -bare;
}
constexpr MsgId operator++() noexcept {
return ++bare;
}
constexpr MsgId operator++(int) noexcept {
return bare++;
}
constexpr MsgId operator--() noexcept {
return --bare;
}
constexpr MsgId operator--(int) noexcept {
return bare--;
}
int64 bare = 0;
};
Q_DECLARE_METATYPE(MsgId);
[[nodiscard]] inline constexpr MsgId operator+(MsgId a, MsgId b) noexcept {
return MsgId(a.bare + b.bare);
}
[[nodiscard]] inline constexpr MsgId operator-(MsgId a, MsgId b) noexcept {
return MsgId(a.bare - b.bare);
}
[[nodiscard]] inline constexpr bool operator==(MsgId a, MsgId b) noexcept {
return (a.bare == b.bare);
}
[[nodiscard]] inline constexpr bool operator!=(MsgId a, MsgId b) noexcept {
return (a.bare != b.bare);
}
[[nodiscard]] inline constexpr bool operator<(MsgId a, MsgId b) noexcept {
return (a.bare < b.bare);
}
[[nodiscard]] inline constexpr bool operator>(MsgId a, MsgId b) noexcept {
return (a.bare > b.bare);
}
[[nodiscard]] inline constexpr bool operator<=(MsgId a, MsgId b) noexcept {
return (a.bare <= b.bare);
}
[[nodiscard]] inline constexpr bool operator>=(MsgId a, MsgId b) noexcept {
return (a.bare >= b.bare);
}
constexpr auto StartClientMsgId = MsgId(0x01 - (1LL << 58));
constexpr auto EndClientMsgId = MsgId(-(1LL << 57));
constexpr auto ServerMaxMsgId = MsgId(1LL << 56);
constexpr auto ShowAtUnreadMsgId = MsgId(0);
constexpr auto SpecialMsgIdShift = EndClientMsgId.bare;
constexpr auto ShowAtTheEndMsgId = MsgId(SpecialMsgIdShift + 1);
constexpr auto SwitchAtTopMsgId = MsgId(SpecialMsgIdShift + 2);
constexpr auto ShowAtProfileMsgId = MsgId(SpecialMsgIdShift + 3);
constexpr auto ShowAndStartBotMsgId = MsgId(SpecialMsgIdShift + 4);
constexpr auto ShowAtGameShareMsgId = MsgId(SpecialMsgIdShift + 5);
constexpr auto ShowForChooseMessagesMsgId = MsgId(SpecialMsgIdShift + 6);
static_assert(SpecialMsgIdShift + 0xFF < 0);
static_assert(-(SpecialMsgIdShift + 0xFF) > ServerMaxMsgId);
[[nodiscard]] constexpr inline bool IsClientMsgId(MsgId id) noexcept {
return (id >= StartClientMsgId && id < EndClientMsgId);
}
[[nodiscard]] constexpr inline bool IsServerMsgId(MsgId id) noexcept {
return (id > 0 && id < ServerMaxMsgId);
}
struct MsgRange {
constexpr MsgRange() noexcept = default;
constexpr MsgRange(MsgId from, MsgId till) noexcept
: from(from)
, till(till) {
}
MsgId from = 0;
MsgId till = 0;
};
[[nodiscard]] inline constexpr bool operator==(
MsgRange a,
MsgRange b) noexcept {
return (a.from == b.from) && (a.till == b.till);
}
[[nodiscard]] inline constexpr bool operator!=(
MsgRange a,
MsgRange b) noexcept {
return !(a == b);
}
struct FullMsgId {
constexpr FullMsgId() noexcept = default;
constexpr FullMsgId(ChannelId channel, MsgId msg) noexcept
: channel(channel), msg(msg) {
}
constexpr explicit operator bool() const noexcept {
return msg != 0;
}
constexpr bool operator!() const noexcept {
return msg == 0;
}
ChannelId channel = NoChannel;
MsgId msg = 0;
};
[[nodiscard]] inline constexpr bool operator<(
const FullMsgId &a,
const FullMsgId &b) noexcept {
if (a.channel < b.channel) {
return true;
} else if (a.channel > b.channel) {
return false;
}
return a.msg < b.msg;
}
[[nodiscard]] inline constexpr bool operator>(
const FullMsgId &a,
const FullMsgId &b) noexcept {
return b < a;
}
[[nodiscard]] inline constexpr bool operator<=(
const FullMsgId &a,
const FullMsgId &b) noexcept {
return !(b < a);
}
[[nodiscard]] inline constexpr bool operator>=(
const FullMsgId &a,
const FullMsgId &b) noexcept {
return !(a < b);
}
[[nodiscard]] inline constexpr bool operator==(
const FullMsgId &a,
const FullMsgId &b) noexcept {
return (a.channel == b.channel) && (a.msg == b.msg);
}
[[nodiscard]] inline constexpr bool operator!=(
const FullMsgId &a,
const FullMsgId &b) noexcept {
return !(a == b);
}
Q_DECLARE_METATYPE(FullMsgId);
namespace std {
template <>
struct hash<MsgId> : private hash<int64> {
size_t operator()(MsgId value) const noexcept {
return hash<int64>::operator()(value.bare);
}
};
} // namespace std

View File

@@ -1004,19 +1004,24 @@ PeerId PeerData::groupCallDefaultJoinAs() const {
return 0;
}
void PeerData::setThemeEmoji(const QString &emoji) {
if (_themeEmoji == emoji) {
void PeerData::setThemeEmoji(const QString &emoticon) {
if (_themeEmoticon == emoticon) {
return;
}
_themeEmoji = emoji;
if (!emoji.isEmpty() && !owner().cloudThemes().themeForEmoji(emoji)) {
if (Ui::Emoji::Find(_themeEmoticon) == Ui::Emoji::Find(emoticon)) {
_themeEmoticon = emoticon;
return;
}
_themeEmoticon = emoticon;
if (!emoticon.isEmpty()
&& !owner().cloudThemes().themeForEmoji(emoticon)) {
owner().cloudThemes().refreshChatThemes();
}
session().changes().peerUpdated(this, UpdateFlag::ChatThemeEmoji);
}
const QString &PeerData::themeEmoji() const {
return _themeEmoji;
return _themeEmoticon;
}
void PeerData::setIsBlocked(bool is) {

View File

@@ -459,7 +459,7 @@ public:
[[nodiscard]] Data::GroupCall *groupCall() const;
[[nodiscard]] PeerId groupCallDefaultJoinAs() const;
void setThemeEmoji(const QString &emoji);
void setThemeEmoji(const QString &emoticon);
[[nodiscard]] const QString &themeEmoji() const;
const PeerId id;
@@ -506,7 +506,7 @@ private:
LoadedStatus _loadedStatus = LoadedStatus::Not;
QString _about;
QString _themeEmoji;
QString _themeEmoticon;
};

View File

@@ -29,7 +29,7 @@ constexpr auto kMessagesPerPage = 50;
TimeId date,
const QString &text) {
return history->makeServiceMessage(
history->session().data().nextNonHistoryEntryId(),
history->nextNonHistoryEntryId(),
MessageFlag::FakeHistoryItem,
date,
HistoryService::PreparedText{ text });
@@ -101,7 +101,7 @@ rpl::producer<MessagesSlice> RepliesList::source(
_partLoaded.events(
) | rpl::start_with_next(pushDelayed, lifetime);
_history->session().data().channelDifferenceTooLong(
_history->owner().channelDifferenceTooLong(
) | rpl::filter([=](not_null<ChannelData*> channel) {
if (_history->peer != channel || !_skippedAfter.has_value()) {
return false;

View File

@@ -32,18 +32,18 @@ constexpr auto kRequestTimeLimit = 60 * crl::time(1000);
&& (item->date() > base::unixtime::now());
}
MTPMessage PrepareMessage(const MTPMessage &message, MsgId id) {
MTPMessage PrepareMessage(const MTPMessage &message) {
return message.match([&](const MTPDmessageEmpty &data) {
return MTP_messageEmpty(
data.vflags(),
MTP_int(id),
data.vid(),
data.vpeer_id() ? *data.vpeer_id() : MTPPeer());
}, [&](const MTPDmessageService &data) {
return MTP_messageService(
MTP_flags(data.vflags().v
| MTPDmessageService::Flag(
MTPDmessage::Flag::f_from_scheduled)),
MTP_int(id),
data.vid(),
data.vfrom_id() ? *data.vfrom_id() : MTPPeer(),
data.vpeer_id(),
data.vreply_to() ? *data.vreply_to() : MTPMessageReplyHeader(),
@@ -53,7 +53,7 @@ MTPMessage PrepareMessage(const MTPMessage &message, MsgId id) {
}, [&](const MTPDmessage &data) {
return MTP_message(
MTP_flags(data.vflags().v | MTPDmessage::Flag::f_from_scheduled),
MTP_int(id),
data.vid(),
data.vfrom_id() ? *data.vfrom_id() : MTPPeer(),
data.vpeer_id(),
data.vfwd_from() ? *data.vfwd_from() : MTPMessageFwdHeader(),
@@ -192,6 +192,7 @@ void ScheduledMessages::sendNowSimpleMessage(
const auto views = 1;
const auto forwards = 0;
history->addNewMessage(
update.vid().v,
MTP_message(
MTP_flags(flags),
update.vid(),
@@ -269,7 +270,8 @@ void ScheduledMessages::checkEntitiesAndUpdate(const MTPDmessage &data) {
qs(data.vmessage()),
Api::EntitiesFromMTP(_session, data.ventities().value_or_empty())
}, data.vmedia());
existing->updateReplyMarkup(data.vreply_markup());
existing->updateReplyMarkup(
HistoryMessageMarkupData(data.vreply_markup()));
existing->updateForwardedInfo(data.vfwd_from());
_session->data().requestItemTextRefresh(existing);
@@ -454,7 +456,8 @@ HistoryItem *ScheduledMessages::append(
_session,
data.ventities().value_or_empty())
}, data.vmedia());
existing->updateReplyMarkup(data.vreply_markup());
existing->updateReplyMarkup(
HistoryMessageMarkupData(data.vreply_markup()));
existing->updateForwardedInfo(data.vfwd_from());
existing->updateDate(data.vdate().v);
history->owner().requestItemTextRefresh(existing);
@@ -463,7 +466,8 @@ HistoryItem *ScheduledMessages::append(
}
const auto item = _session->data().addNewMessage(
PrepareMessage(message, history->nextNonHistoryEntryId()),
history->nextNonHistoryEntryId(),
PrepareMessage(message),
MessageFlags(), // localFlags
NewMessageType::Existing);
if (!item || item->history() != history) {
@@ -546,7 +550,7 @@ uint64 ScheduledMessages::countListHash(const List &list) const {
}) | ranges::views::reverse;
for (const auto &item : serverside) {
const auto j = list.idByItem.find(item.get());
HashUpdate(hash, j->second);
HashUpdate(hash, j->second.bare);
if (const auto edited = item->Get<HistoryMessageEdited>()) {
HashUpdate(hash, edited->date);
} else {

View File

@@ -60,7 +60,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_info.h"
#include "base/unixtime.h"
#include "base/call_delayed.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include "facades.h" // Notify::switchInlineBotButtonReceived
#include "app.h"
#include "styles/style_boxes.h" // st::backgroundSize
@@ -95,7 +95,7 @@ void CheckForSwitchInlineButton(not_null<HistoryItem*> item) {
return;
}
if (const auto markup = item->Get<HistoryMessageReplyMarkup>()) {
for (const auto &row : markup->rows) {
for (const auto &row : markup->data.rows) {
for (const auto &button : row) {
using ButtonType = HistoryMessageMarkupButton::Type;
if (button.type == ButtonType::SwitchInline) {
@@ -1382,6 +1382,10 @@ void Session::requestItemRepaint(not_null<const HistoryItem*> item) {
}
}
}
const auto history = item->history();
if (history->lastItemDialogsView.dependsOn(item)) {
history->updateChatListEntry();
}
}
rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const {
@@ -1838,8 +1842,8 @@ void Session::processMessages(
continue;
}
}
const auto id = IdFromMessage(message);
indices.emplace((uint64(uint32(id)) << 32) | uint64(i), i);
const auto id = IdFromMessage(message); // Only 32 bit values here.
indices.emplace((uint64(uint32(id.bare)) << 32) | uint64(i), i);
}
for (const auto &[position, index] : indices) {
addNewMessage(
@@ -1855,6 +1859,26 @@ void Session::processMessages(
processMessages(data.v, type);
}
void Session::processExistingMessages(
ChannelData *channel,
const MTPmessages_Messages &data) {
data.match([&](const MTPDmessages_channelMessages &data) {
if (channel) {
channel->ptsReceived(data.vpts().v);
} else {
LOG(("App Error: received messages.channelMessages!"));
}
}, [](const auto &) {});
data.match([&](const MTPDmessages_messagesNotModified&) {
LOG(("API Error: received messages.messagesNotModified!"));
}, [&](const auto &data) {
processUsers(data.vusers());
processChats(data.vchats());
processMessages(data.vmessages(), NewMessageType::Existing);
});
}
const Session::Messages *Session::messagesList(ChannelId channelId) const {
if (channelId == NoChannel) {
return &_messages;
@@ -2166,12 +2190,21 @@ HistoryItem *Session::addNewMessage(
const MTPMessage &data,
MessageFlags localFlags,
NewMessageType type) {
return addNewMessage(IdFromMessage(data), data, localFlags, type);
}
HistoryItem *Session::addNewMessage(
MsgId id,
const MTPMessage &data,
MessageFlags localFlags,
NewMessageType type) {
const auto peerId = PeerFromMessage(data);
if (!peerId) {
return nullptr;
}
const auto result = history(peerId)->addNewMessage(
id,
data,
localFlags,
type);
@@ -2427,7 +2460,7 @@ PhotoData *Session::photoFromWeb(
return nullptr;
}
return photo(
openssl::RandomValue<PhotoId>(),
base::RandomValue<PhotoId>(),
uint64(0),
QByteArray(),
base::unixtime::now(),
@@ -2692,7 +2725,7 @@ DocumentData *Session::documentFromWeb(
const ImageLocation &thumbnailLocation,
const ImageLocation &videoThumbnailLocation) {
const auto result = document(
openssl::RandomValue<DocumentId>(),
base::RandomValue<DocumentId>(),
uint64(0),
QByteArray(),
base::unixtime::now(),
@@ -2714,7 +2747,7 @@ DocumentData *Session::documentFromWeb(
const ImageLocation &thumbnailLocation,
const ImageLocation &videoThumbnailLocation) {
const auto result = document(
openssl::RandomValue<DocumentId>(),
base::RandomValue<DocumentId>(),
uint64(0),
QByteArray(),
base::unixtime::now(),

View File

@@ -341,6 +341,9 @@ public:
void processMessages(
const MTPVector<MTPMessage> &data,
NewMessageType type);
void processExistingMessages(
ChannelData *channel,
const MTPmessages_Messages &data);
void processMessagesDeleted(
ChannelId channelId,
const QVector<MTPint> &data);
@@ -397,6 +400,11 @@ public:
const MTPMessage &data,
MessageFlags localFlags,
NewMessageType type);
HistoryItem *addNewMessage( // Override message id.
MsgId id,
const MTPMessage &data,
MessageFlags localFlags,
NewMessageType type);
[[nodiscard]] int unreadBadge() const;
[[nodiscard]] bool unreadBadgeMuted() const;

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