Compare commits

...

161 Commits

Author SHA1 Message Date
John Preston
ae261fcede Beta version 3.1.7.
- Fix channel message views and comments counter updates.
- Sponsored messages support.
- Crash fix.
2021-10-07 11:42:24 +04:00
John Preston
c04cdff7f7 Disable group call logs to console. 2021-10-07 11:42:24 +04:00
John Preston
466aa5a14d Fix view button style. 2021-10-07 11:38:07 +04:00
John Preston
4aac633413 Fix views increment.
Regression was introduced in 21aa1f49d7.

Fixes #17069.
2021-10-07 11:29:30 +04:00
23rd
ad328d35a2 Added box for ad description. 2021-10-07 11:09:09 +04:00
23rd
c5140f34a7 Added view button to sponsored messages. 2021-10-07 11:09:09 +04:00
23rd
419f6345b3 Added sponsored messages to HistoryWidget. 2021-10-07 11:09:09 +04:00
23rd
c2c53df886 Added new Ui::СontinuousScroll. 2021-10-07 11:09:09 +04:00
23rd
b3f73bb6a9 Added badge for sponsored messages. 2021-10-07 11:09:08 +04:00
23rd
eda5cd47ad Added manager of sponsored messages. 2021-10-07 11:09:08 +04:00
John Preston
0c906a5e6d Fix crash in local changelog messages. 2021-10-07 10:58:24 +04:00
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
318 changed files with 7732 additions and 2847 deletions

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,432 +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/local
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=$LibrariesPath/opus-cache
make -j$(nproc)
make install
- name: Opus install.
run: |
cd $LibrariesPath
cp -R opus-cache/. local/
- 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=$LibrariesPath/libiconv-cache
make -j$(nproc)
make install
- name: Libiconv install.
run: |
cd $LibrariesPath
cp -R libiconv-cache/. local/
- 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" \
--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

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

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
@@ -160,6 +174,8 @@ PRIVATE
boxes/peers/edit_peer_permissions_box.h
boxes/about_box.cpp
boxes/about_box.h
boxes/about_sponsored_box.cpp
boxes/about_sponsored_box.h
boxes/abstract_box.cpp
boxes/abstract_box.h
boxes/add_contact_box.cpp
@@ -278,6 +294,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 +422,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
@@ -436,6 +455,8 @@ PRIVATE
data/data_shared_media.h
data/data_sparse_ids.cpp
data/data_sparse_ids.h
data/data_sponsored_messages.cpp
data/data_sponsored_messages.h
data/data_streaming.cpp
data/data_streaming.h
data/data_types.cpp
@@ -456,8 +477,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 +489,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 +604,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
@@ -608,6 +633,8 @@ PRIVATE
history/view/history_view_service_message.h
history/view/history_view_top_bar_widget.cpp
history/view/history_view_top_bar_widget.h
history/view/history_view_view_button.cpp
history/view/history_view_view_button.h
history/view/history_view_webpage_preview.cpp
history/view/history_view_webpage_preview.h
history/history.cpp
@@ -618,6 +645,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 +959,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 +1058,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
@@ -1355,12 +1388,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

@@ -1335,6 +1335,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_forwarded_imported" = "This message was imported from another app. It may not be real.";
"lng_signed_author" = "Author: {user}";
"lng_in_reply_to" = "In reply to";
"lng_sponsored" = "sponsored";
"lng_edited" = "edited";
"lng_edited_date" = "Edited: {date}";
"lng_sent_date" = "Sent: {date}";
@@ -1618,6 +1619,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";
@@ -2238,7 +2240,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}.";
@@ -2869,6 +2872,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";
@@ -2878,6 +2889,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_voice_speed_fast" = "Fast";
"lng_voice_speed_very_fast" = "Very fast";
"lng_view_button_user" = "View user";
"lng_view_button_bot" = "View bot";
"lng_view_button_group" = "View group";
"lng_view_button_channel" = "View channel";
"lng_sponsored_title" = "What are sponsored messages?";
"lng_sponsored_info_description1" = "Unlike other apps, Telegram never uses your private data to target ads. You are seeing this message only because someone chose this public one-to-many channel as a space to promote their messages. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored message.";
"lng_sponsored_info_description2" = "Unlike other apps, Telegram doesn\'t track whether you tapped on a sponsored message and doesn\'t profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties cant spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.";
"lng_sponsored_info_description3" = "Telegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
"lng_sponsored_info_description4" = "Ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate together.";
// Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program...";

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.4.0" />
Version="3.1.7.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,4,0
PRODUCTVERSION 3,0,4,0
FILEVERSION 3,1,7,0
PRODUCTVERSION 3,1,7,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.4.0"
VALUE "FileVersion", "3.1.7.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "3.0.4.0"
VALUE "ProductVersion", "3.1.7.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,0,4,0
PRODUCTVERSION 3,0,4,0
FILEVERSION 3,1,7,0
PRODUCTVERSION 3,1,7,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.4.0"
VALUE "FileVersion", "3.1.7.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "3.0.4.0"
VALUE "ProductVersion", "3.1.7.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()
@@ -434,7 +439,7 @@ void SendConfirmedFile(
| (localEntities.v.isEmpty()
? MTPDmessage::Flag()
: MTPDmessage::Flag::f_entities)),
MTP_int(newId.msg),
MTP_int(0), // Not used (would've been trimmed to 32 bits).
peerToMTP(messageFromId),
peerToMTP(file->to.peer),
MTPMessageFwdHeader(),
@@ -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,135 @@
/*
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())
, _incrementTimer([=] { viewsIncrement(); }) {
}
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

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

@@ -0,0 +1,76 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/about_sponsored_box.h"
#include "lang/lang_keys.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "styles/style_boxes.h"
#include "styles/style_layers.h"
#include <QtGui/QDesktopServices>
namespace Ui {
namespace {
constexpr auto kUrl = "https://telegram.org/ads"_cs;
} // namespace
void AboutSponsoredBox(not_null<Ui::GenericBox*> box) {
box->setTitle(tr::lng_sponsored_title());
box->setWidth(st::boxWideWidth);
box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); });
const auto addUrl = [&] {
const auto &st = st::sponsoredUrlButton;
const auto row = box->addRow(object_ptr<RpWidget>(box));
row->resize(0, st.height + st.padding.top() + st.padding.bottom());
const auto button = Ui::CreateChild<RoundButton>(
row,
rpl::single<QString>(kUrl.utf8()),
st);
button->setBrushOverride(Qt::NoBrush);
button->setPenOverride(QPen(st::historyLinkInFg));
button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
rpl::combine(
row->sizeValue(),
button->sizeValue()
) | rpl::start_with_next([=](
const QSize &rowSize,
const QSize &buttonSize) {
button->moveToLeft(
(rowSize.width() - buttonSize.width()) / 2,
(rowSize.height() - buttonSize.height()) / 2);
}, row->lifetime());
button->addClickHandler([=] {
QDesktopServices::openUrl({ kUrl.utf8() });
});
};
const auto &stLabel = st::aboutLabel;
const auto info1 = box->addRow(object_ptr<FlatLabel>(box, stLabel));
info1->setText(tr::lng_sponsored_info_description1(tr::now));
box->addSkip(st::sponsoredInfoSkip);
const auto info2 = box->addRow(object_ptr<FlatLabel>(box, stLabel));
info2->setText(tr::lng_sponsored_info_description2(tr::now));
box->addSkip(st::sponsoredInfoSkip);
const auto info3 = box->addRow(object_ptr<FlatLabel>(box, stLabel));
info3->setText(tr::lng_sponsored_info_description3(tr::now));
box->addSkip(st::sponsoredUrlButtonSkip);
addUrl();
box->addSkip(st::sponsoredUrlButtonSkip);
const auto info4 = box->addRow(object_ptr<FlatLabel>(box, stLabel));
info4->setText(tr::lng_sponsored_info_description4(tr::now));
}
} // namespace Ui

View File

@@ -0,0 +1,16 @@
/*
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 "ui/layers/generic_box.h"
namespace Ui {
void AboutSponsoredBox(not_null<Ui::GenericBox*> box);
} // namespace Ui

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

@@ -973,3 +973,22 @@ autolockTimeField: InputField(scheduleTimeField) {
heightMin: 20px;
}
autolockTimeWidth: 52px;
sponsoredInfoSkip: 22px;
sponsoredUrlButtonSkip: 11px;
sponsoredUrlButton: RoundButton(defaultActiveButton) {
height: 32px;
width: -42px;
textBg: transparent;
textBgOver: transparent;
radius: roundRadiusLarge;
padding: margins(2px, 2px, 2px, 2px);
textFg: historyLinkInFg;
textFgOver: historyLinkInFg;
textTop: 7px;
font: normalFont;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}

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"
@@ -1008,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) {
@@ -2361,6 +2361,7 @@ bool GroupCall::tryCreateController() {
auto callLogFolder = cWorkingDir() + qsl("DebugLogs");
auto callLogPath = callLogFolder + qsl("/last_group_call_log.txt");
auto callLogNative = QDir::toNativeSeparators(callLogPath);
descriptor.config.need_log = true;
#ifdef Q_OS_WIN
descriptor.config.logPath.data = callLogNative.toStdWString();
#else // Q_OS_WIN
@@ -2370,6 +2371,8 @@ bool GroupCall::tryCreateController() {
#endif // Q_OS_WIN
QFile(callLogPath).remove();
QDir().mkpath(callLogFolder);
} else {
descriptor.config.need_log = false;
}
LOG(("Call Info: Creating group instance"));

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

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

@@ -54,6 +54,48 @@ std::map<int, const char*> BetaLogs() {
"- 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

@@ -617,7 +617,7 @@ public:
[[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) {

View File

@@ -35,6 +35,7 @@ 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"
@@ -469,6 +470,100 @@ 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,
@@ -485,6 +580,9 @@ bool ResolveTestChatTheme(
history->peer->themeEmoji(),
params);
if (theme) {
if (!params["export"].isEmpty()) {
ExportTestChatTheme(&controller->session(), &*theme);
}
[[maybe_unused]] auto value = controller->cachedChatThemeValue(
*theme);
}

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 = 3000004;
constexpr auto AppVersionStr = "3.0.4";
constexpr auto AppVersion = 3001007;
constexpr auto AppVersionStr = "3.1.7";
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

@@ -387,26 +387,29 @@ 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) {
const QString &emoticon) {
const auto testing = TestingColors();
if (emoji.isEmpty()) {
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(emoji);
return themeForEmoji(emoticon);
}) | rpl::filter([](const std::optional<ChatTheme> &theme) {
return theme.has_value();
}));
@@ -419,7 +422,7 @@ rpl::producer<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue(
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(limit));
@@ -433,7 +436,7 @@ void CloudThemes::SetTestingColors(bool testing) {
IsTestingColors = testing;
}
QString CloudThemes::PrepareTestingLink(const CloudTheme &theme) {
QString CloudThemes::prepareTestingLink(const CloudTheme &theme) const {
const auto hex = [](int value) {
return QChar((value < 10) ? ('0' + value) : ('a' + (value - 10)));
};
@@ -460,6 +463,16 @@ QString CloudThemes::PrepareTestingLink(const CloudTheme &theme) {
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));
}
@@ -472,12 +485,15 @@ QString CloudThemes::PrepareTestingLink(const CloudTheme &theme) {
}
std::optional<CloudTheme> CloudThemes::updateThemeFromLink(
const QString &emoji,
const QString &emoticon,
const QMap<QString, QString> &params) {
if (!TestingColors()) {
const auto emoji = Ui::Emoji::Find(emoticon);
if (!TestingColors() || !emoji) {
return std::nullopt;
}
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);
});
if (i == end(_chatThemes)) {
return std::nullopt;
}
@@ -525,7 +541,11 @@ std::optional<CloudTheme> CloudThemes::updateThemeFromLink(
const auto bg = colors(params["bg"]);
applyTo.paper = (applyTo.paper && !bg.empty())
? std::make_optional(applyTo.paper->withBackgroundColors(bg))
: std::nullopt;
: 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({});
@@ -538,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,15 +71,15 @@ 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]] static QString PrepareTestingLink(const CloudTheme &theme);
[[nodiscard]] QString prepareTestingLink(const CloudTheme &theme) const;
[[nodiscard]] std::optional<CloudTheme> updateThemeFromLink(
const QString &emoji,
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

@@ -53,6 +53,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat_filters.h"
#include "data/data_scheduled_messages.h"
#include "data/data_send_action.h"
#include "data/data_sponsored_messages.h"
#include "data/data_cloud_themes.h"
#include "data/data_streaming.h"
#include "data/data_media_rotation.h"
@@ -60,7 +61,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 +96,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) {
@@ -239,7 +240,8 @@ Session::Session(not_null<Main::Session*> session)
, _streaming(std::make_unique<Streaming>(this))
, _mediaRotation(std::make_unique<MediaRotation>())
, _histories(std::make_unique<Histories>(this))
, _stickers(std::make_unique<Stickers>(this)) {
, _stickers(std::make_unique<Stickers>(this))
, _sponsoredMessages(std::make_unique<SponsoredMessages>(this)) {
_cache->open(_session->local().cacheKey());
_bigFileCache->open(_session->local().cacheBigFileKey());
@@ -280,6 +282,7 @@ void Session::clear() {
_histories->unloadAll();
_scheduledMessages = nullptr;
_sponsoredMessages = nullptr;
_dependentMessages.clear();
base::take(_messages);
base::take(_channelMessages);
@@ -1382,6 +1385,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 +1845,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 +1862,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 +2193,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 +2463,7 @@ PhotoData *Session::photoFromWeb(
return nullptr;
}
return photo(
openssl::RandomValue<PhotoId>(),
base::RandomValue<PhotoId>(),
uint64(0),
QByteArray(),
base::unixtime::now(),
@@ -2692,7 +2728,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 +2750,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(),
@@ -3948,10 +3984,12 @@ void Session::insertCheckedServiceNotification(
| MessageFlag::LocalHistoryEntry;
auto sending = TextWithEntities(), left = message;
while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
const auto id = nextLocalMessageId();
addNewMessage(
id,
MTP_message(
MTP_flags(flags),
MTP_int(nextLocalMessageId()),
MTP_int(0), // Not used (would've been trimmed to 32 bits).
peerToMTP(PeerData::kServiceNotificationsId),
peerToMTP(PeerData::kServiceNotificationsId),
MTPMessageFwdHeader(),

View File

@@ -51,6 +51,7 @@ class LocationPoint;
class WallPaper;
class ScheduledMessages;
class SendActionManager;
class SponsoredMessages;
class ChatFilters;
class CloudThemes;
class Streaming;
@@ -109,6 +110,9 @@ public:
[[nodiscard]] Stickers &stickers() const {
return *_stickers;
}
[[nodiscard]] SponsoredMessages &sponsoredMessages() const {
return *_sponsoredMessages;
}
[[nodiscard]] MsgId nextNonHistoryEntryId() {
return ++_nonHistoryEntryId;
}
@@ -341,6 +345,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 +404,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;
@@ -955,6 +967,7 @@ private:
std::unique_ptr<MediaRotation> _mediaRotation;
std::unique_ptr<Histories> _histories;
std::unique_ptr<Stickers> _stickers;
std::unique_ptr<SponsoredMessages> _sponsoredMessages;
MsgId _nonHistoryEntryId = ServerMaxMsgId;
rpl::lifetime _lifetime;

View File

@@ -366,7 +366,7 @@ std::optional<int> SharedMediaWithLastSlice::indexOf(Value value) const {
: QString("-"));
if (const auto msgId = std::get_if<FullMsgId>(&value)) {
info.push_back("value:" + QString::number(msgId->channel.bare));
info.push_back(QString::number(msgId->msg));
info.push_back(QString::number(msgId->msg.bare));
const auto index = _slice.indexOf(*std::get_if<FullMsgId>(&value));
info.push_back("index:" + (index
? QString::number(*index)

View File

@@ -0,0 +1,196 @@
/*
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 "data/data_sponsored_messages.h"
#include "api/api_text_entities.h"
#include "apiwrap.h"
#include "base/unixtime.h"
#include "data/data_channel.h"
#include "data/data_peer_id.h"
#include "data/data_session.h"
#include "history/history.h"
#include "main/main_session.h"
namespace Data {
namespace {
constexpr auto kRequestTimeLimit = 5 * 60 * crl::time(1000);
[[nodiscard]] bool TooEarlyForRequest(crl::time received) {
return (received > 0) && (received + kRequestTimeLimit > crl::now());
}
} // namespace
SponsoredMessages::SponsoredMessages(not_null<Session*> owner)
: _session(&owner->session())
, _clearTimer([=] { clearOldRequests(); }) {
}
SponsoredMessages::~SponsoredMessages() {
for (const auto &request : _requests) {
_session->api().request(request.second.requestId).cancel();
}
}
void SponsoredMessages::clearOldRequests() {
const auto now = crl::now();
while (true) {
const auto i = ranges::find_if(_requests, [&](const auto &value) {
const auto &request = value.second;
return !request.requestId
&& (request.lastReceived + kRequestTimeLimit <= now);
});
if (i == end(_requests)) {
break;
}
_requests.erase(i);
}
}
bool SponsoredMessages::append(not_null<History*> history) {
const auto it = _data.find(history);
if (it == end(_data)) {
return false;
}
auto &list = it->second;
if (list.showedAll) {
return false;
}
const auto entryIt = ranges::find_if(list.entries, [](const Entry &e) {
return e.item == nullptr;
});
if (entryIt == end(list.entries)) {
list.showedAll = true;
return false;
}
const auto flags = MessageFlags(0)
| (history->isChannel() ? MessageFlag::Post : MessageFlags(0))
| MessageFlag::HasFromId
| MessageFlag::IsSponsored
| MessageFlag::LocalHistoryEntry;
auto local = history->addNewLocalMessage(
_session->data().nextLocalMessageId(),
flags,
UserId(0),
MsgId(0),
HistoryItem::NewMessageDate(0),
entryIt->sponsored.fromId,
QString(),
entryIt->sponsored.textWithEntities,
MTP_messageMediaEmpty(),
HistoryMessageMarkupData());
entryIt->item.reset(std::move(local));
// Since sponsored posts are only created on demand for display,
// we can send a request to view immediately.
view(entryIt);
return true;
}
void SponsoredMessages::request(not_null<History*> history) {
auto &request = _requests[history];
if (request.requestId || TooEarlyForRequest(request.lastReceived)) {
return;
}
request.requestId = _session->api().request(
MTPchannels_GetSponsoredMessages(
_session->data().channel(history->channelId())->inputChannel
)).done([=](const MTPmessages_sponsoredMessages &result) {
parse(history, result);
}).fail([=](const MTP::Error &error) {
_requests.remove(history);
}).send();
}
void SponsoredMessages::parse(
not_null<History*> history,
const MTPmessages_sponsoredMessages &list) {
auto &request = _requests[history];
request.lastReceived = crl::now();
request.requestId = 0;
if (!_clearTimer.isActive()) {
_clearTimer.callOnce(kRequestTimeLimit * 2);
}
list.match([&](const MTPDmessages_sponsoredMessages &data) {
_session->data().processUsers(data.vusers());
_session->data().processChats(data.vchats());
const auto &messages = data.vmessages().v;
if (messages.isEmpty()) {
return;
}
auto &list = _data.emplace(history, List()).first->second;
list.entries.clear();
for (const auto &message : messages) {
append(history, list, message);
}
});
}
void SponsoredMessages::append(
not_null<History*> history,
List &list,
const MTPSponsoredMessage &message) {
message.match([&](const MTPDsponsoredMessage &data) {
const auto randomId = data.vrandom_id().v;
auto sharedMessage = SponsoredMessage{
.randomId = randomId,
.fromId = peerFromMTP(data.vfrom_id()),
.textWithEntities = {
.text = qs(data.vmessage()),
.entities = Api::EntitiesFromMTP(
_session,
data.ventities().value_or_empty()),
},
.history = history,
};
list.entries.push_back({ nullptr, std::move(sharedMessage) });
});
}
void SponsoredMessages::clearItems(not_null<History*> history) {
const auto it = _data.find(history);
if (it == end(_data)) {
return;
}
auto &list = it->second;
for (auto &entry : list.entries) {
entry.item.reset();
}
list.showedAll = false;
}
void SponsoredMessages::view(const std::vector<Entry>::iterator entryIt) {
const auto randomId = entryIt->sponsored.randomId;
auto &request = _viewRequests[randomId];
if (request.requestId || TooEarlyForRequest(request.lastReceived)) {
return;
}
const auto history = entryIt->sponsored.history;
if (!history) {
return;
}
request.requestId = _session->api().request(
MTPchannels_ViewSponsoredMessage(
_session->data().channel(history->channelId())->inputChannel,
MTP_bytes(randomId)
)).done([=] {
auto &request = _viewRequests[randomId];
request.lastReceived = crl::now();
request.requestId = 0;
}).fail([=](const MTP::Error &error) {
_viewRequests.remove(randomId);
}).send();
}
} // namespace Data

View File

@@ -0,0 +1,79 @@
/*
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 "history/history_item.h"
#include "base/timer.h"
class History;
namespace Main {
class Session;
} // namespace Main
namespace Data {
class Session;
struct SponsoredMessage final {
QByteArray randomId;
PeerId fromId;
TextWithEntities textWithEntities;
History *history = nullptr;
};
class SponsoredMessages final {
public:
using RandomId = QByteArray;
explicit SponsoredMessages(not_null<Session*> owner);
SponsoredMessages(const SponsoredMessages &other) = delete;
SponsoredMessages &operator=(const SponsoredMessages &other) = delete;
~SponsoredMessages();
void request(not_null<History*> history);
[[nodiscard]] bool append(not_null<History*> history);
void clearItems(not_null<History*> history);
private:
using OwnedItem = std::unique_ptr<HistoryItem, HistoryItem::Destroyer>;
struct Entry {
OwnedItem item;
SponsoredMessage sponsored;
};
struct List {
std::vector<Entry> entries;
bool showedAll = false;
};
struct Request {
mtpRequestId requestId = 0;
crl::time lastReceived = 0;
};
void parse(
not_null<History*> history,
const MTPmessages_sponsoredMessages &list);
void append(
not_null<History*> history,
List &list,
const MTPSponsoredMessage &message);
void clearOldRequests();
void view(const std::vector<Entry>::iterator entryIt);
const not_null<Main::Session*> _session;
base::Timer _clearTimer;
base::flat_map<not_null<History*>, List> _data;
base::flat_map<not_null<History*>, Request> _requests;
base::flat_map<RandomId, Request> _viewRequests;
rpl::lifetime _lifetime;
};
} // namespace Data

View File

@@ -80,8 +80,9 @@ Storage::Cache::Key GeoPointCacheKey(const GeoPointLocation &location) {
| (uint32(location.height) & 0xFFFFU);
return Storage::Cache::Key{
Data::kGeoPointCacheTag | (uint64(zoomscale) << 32) | widthheight,
(uint64(std::round(std::abs(location.lat + 360.) * 1000000)) << 32)
| uint64(std::round(std::abs(location.lon + 360.) * 1000000))
(uint64(base::SafeRound(
std::abs(location.lat + 360.) * 1000000)) << 32)
| uint64(base::SafeRound(std::abs(location.lon + 360.) * 1000000))
};
}

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