Compare commits

...

116 Commits

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

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

View File

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

View File

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

View File

@@ -58,7 +58,7 @@ jobs:
UPLOAD_ARTIFACT: "false"
ONLY_CACHE: "false"
MANUAL_CACHING: "0"
DOC_PATH: "docs/building-win.md"
PREPARE_PATH: "Telegram/build/prepare/prepare.py"
AUTO_CACHING: "1"
defaults:
@@ -132,24 +132,12 @@ jobs:
working-directory: ${{ github.workspace }}
run: |
echo "Find necessary commit from doc."
checkoutCommit=$(grep -A 1 "cd patches" $REPO_NAME/$DOC_PATH | sed -n 2p)
checkoutCommit=$(grep -A 1 "cd patches" $REPO_NAME/$PREPARE_PATH | sed -n 2p)
cd $LibrariesPath
git clone $GIT/desktop-app/patches.git
cd Patches
cd patches
eval $checkoutCommit
- name: Find any version of Python 2.
shell: bash
run: |
echo "Find any version of Python 2."
p=`ls /c/hostedtoolcache/windows/python | grep "^2" | tail -1`
if [ -z "$p" ]; then
echo "Python 2 is not found."
exit 1
fi
echo "PY2=C:\\hostedtoolcache\\windows\\Python\\$p\\x64" >> $GITHUB_ENV
echo "Found $p."
- name: LZMA.
run: |
git clone %GIT%/telegramdesktop/lzma.git
@@ -161,28 +149,27 @@ jobs:
id: cache-openssl
uses: actions/cache@v2
with:
path: ${{ env.LibrariesPath }}/openssl_${{ env.OPENSSL_VER }}
path: ${{ env.LibrariesPath }}/openssl
key: ${{ runner.OS }}-${{ env.CACHE_KEY }}-${{ env.OPENSSL_VER }}
- name: OpenSSL.
if: steps.cache-openssl.outputs.cache-hit != 'true'
run: |
git clone %GIT%/openssl/openssl.git openssl_%OPENSSL_VER%
cd openssl_%OPENSSL_VER%
git checkout OpenSSL_%OPENSSL_VER%-stable
git clone -b OpenSSL_%OPENSSL_VER%-stable %GIT%/openssl/openssl.git
cd openssl
perl Configure no-shared no-tests debug-VC-WIN32
nmake
mkdir out32.dbg
move libcrypto.lib out32.dbg
move libssl.lib out32.dbg
move ossl_static.pdb out32.dbg\ossl_static
mkdir out.dbg
move libcrypto.lib out.dbg
move libssl.lib out.dbg
move ossl_static.pdb out.dbg\ossl_static
nmake clean
move out32.dbg\ossl_static out32.dbg\ossl_static.pdb
move out.dbg\ossl_static out.dbg\ossl_static.pdb
perl Configure no-shared no-tests VC-WIN32
nmake
mkdir out32
move libcrypto.lib out32
move libssl.lib out32
move ossl_static.pdb out32
mkdir out
move libcrypto.lib out
move libssl.lib out
move ossl_static.pdb out
rmdir /S /Q test
rmdir /S /Q .git
@@ -239,16 +226,17 @@ jobs:
GYP_MSVS_VERSION: 2019
if: steps.cache-breakpad.outputs.cache-hit != 'true'
run: |
git clone %GIT%/telegramdesktop/gyp.git
git clone https://chromium.googlesource.com/external/gyp
cd gyp
SET PATH=%PY2%;%cd%;%PATH%
git checkout tdesktop
SET PATH=%cd%;%PATH%
git checkout d6c5dd51dc
git apply ../patches/gyp.diff
cd %LibrariesPath%
git clone %GIT%/google/breakpad
git clone https://chromium.googlesource.com/breakpad/breakpad
cd breakpad
git checkout a1dbcdcb43
git checkout bc8fb886
git apply ../patches/breakpad.diff
cd src
git clone %GIT%/google/googletest testing
@@ -258,10 +246,9 @@ jobs:
ninja -C out/Debug common crash_generation_client exception_handler
ninja -C out/Release common crash_generation_client exception_handler
cd tools\windows\dump_syms
call gyp dump_syms.gyp
call vcvars32.bat
msbuild -m dump_syms.vcxproj /property:Configuration=Release
call gyp dump_syms.gyp --format=ninja
cd ..\..\..
ninja -C out/Release dump_syms
- name: Opus cache.
id: cache-opus
@@ -272,12 +259,17 @@ jobs:
- name: Opus.
if: steps.cache-opus.outputs.cache-hit != 'true'
run: |
git clone %GIT%/telegramdesktop/opus.git
git clone -b v1.3.1 %GIT%/xiph/opus.git
cd opus
git checkout tdesktop
cd win32\VS2015
msbuild -m opus.sln /property:Configuration=Debug /property:Platform="Win32"
msbuild -m opus.sln /property:Configuration=Release /property:Platform="Win32"
git cherry-pick 927de8453c
cmake -B out . ^
-A Win32 ^
-DCMAKE_INSTALL_PREFIX=%LibrariesPath%/local/opus ^
-DCMAKE_C_FLAGS_DEBUG="/MTd /Zi /Ob0 /Od /RTC1" ^
-DCMAKE_C_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG"
cmake --build out --config Debug
cmake --build out --config Release
cmake --install out --config Release
- name: Rnnoise.
run: |
@@ -352,7 +344,7 @@ jobs:
for /r %%i in (..\..\patches\qtbase_%QT%\*) do git apply %%i
cd ..
SET SSL=%LibrariesPath%\openssl_%OPENSSL_VER%
SET SSL=%LibrariesPath%\openssl
SET SSL_LIBS=libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib
SET ANGLE=%LibrariesPath%\tg_angle
@@ -380,8 +372,8 @@ jobs:
QMAKE_LIBS_EGL_RELEASE="%ANGLE%\out\Release\tg_angle.lib %ZLIB%\ZlibStatReleaseWithoutAsm\zlibstat.lib %ANGLE_LIBS% Gdi32.lib User32.lib" ^
-openssl-linked ^
-I "%SSL%\include" ^
OPENSSL_LIBS_DEBUG="%SSL%\out32.dbg\libssl.lib %SSL%\out32.dbg\%SSL_LIBS%" ^
OPENSSL_LIBS_RELEASE="%SSL%\out32\libssl.lib %SSL%\out32\%SSL_LIBS%" ^
OPENSSL_LIBS_DEBUG="%SSL%\out.dbg\libssl.lib %SSL%\out.dbg\%SSL_LIBS%" ^
OPENSSL_LIBS_RELEASE="%SSL%\out\libssl.lib %SSL%\out\%SSL_LIBS%" ^
-I "%LibrariesPath%\mozjpeg" ^
LIBJPEG_LIBS_DEBUG="%LibrariesPath%\mozjpeg\Debug\jpeg-static.lib" ^
LIBJPEG_LIBS_RELEASE="%LibrariesPath%\mozjpeg\Release\jpeg-static.lib" ^
@@ -418,7 +410,7 @@ jobs:
-DTG_OWT_SPECIAL_TARGET=win ^
-DTG_OWT_BUILD_AUDIO_BACKENDS=OFF ^
-DTG_OWT_LIBJPEG_INCLUDE_PATH=%cd%/../../../mozjpeg ^
-DTG_OWT_OPENSSL_INCLUDE_PATH=%cd%/../../../openssl_%OPENSSL_VER%/include ^
-DTG_OWT_OPENSSL_INCLUDE_PATH=%cd%/../../../openssl/include ^
-DTG_OWT_OPUS_INCLUDE_PATH=%cd%/../../../opus/include ^
-DTG_OWT_FFMPEG_INCLUDE_PATH=%cd%/../../../ffmpeg ^
../..

View File

@@ -43,6 +43,8 @@ include(cmake/generate_appdata_changelog.cmake)
if (WIN32)
include(cmake/generate_midl.cmake)
generate_midl(Telegram ${src_loc}/platform/win/windows_quiethours.idl)
nuget_add_winrt(Telegram)
endif()
set_target_properties(Telegram PROPERTIES AUTOMOC ON AUTORCC ON)
@@ -52,6 +54,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 +81,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
@@ -278,6 +287,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
@@ -581,6 +592,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
@@ -1245,17 +1258,13 @@ elseif (APPLE)
endif()
endif()
else()
target_link_libraries(Telegram
PRIVATE
desktop-app::external_glibmm
desktop-app::external_glib
)
if (NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
target_link_libraries(Telegram
PRIVATE
desktop-app::external_statusnotifieritem
desktop-app::external_dbusmenu_qt
desktop-app::external_glibmm
desktop-app::external_glib
)
endif()

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.

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 B

After

Width:  |  Height:  |  Size: 946 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 651 B

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -357,6 +357,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_update_fail" = "Update check failed :(";
"lng_settings_workmode_tray" = "Show tray icon";
"lng_settings_workmode_window" = "Show taskbar icon";
"lng_settings_close_to_taskbar" = "Close to taskbar";
"lng_settings_native_frame" = "Use system window frame";
"lng_settings_auto_start" = "Launch Telegram when system starts";
"lng_settings_start_min" = "Launch minimized";
@@ -1617,6 +1618,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_user_action_upload_file" = "{user} is sending a file";
"lng_send_action_choose_sticker" = "choosing a sticker";
"lng_user_action_choose_sticker" = "{user} is choosing a sticker";
"lng_user_action_watching_animations" = "watching {emoji}";
"lng_unread_bar#one" = "{count} unread message";
"lng_unread_bar#other" = "{count} unread messages";
"lng_unread_bar_some" = "Unread messages";
@@ -2237,7 +2239,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_recording_start_checkbox" = "Also record video";
"lng_group_call_recording_start_audio_subtitle" = "This chat will be recorded into an audio file";
"lng_group_call_recording_start_video_subtitle" = "Choose video orientation";
"lng_group_call_is_recorded" = "Voice chat is being recorded.";
"lng_group_call_is_recorded" = "The audio stream is being recorded.";
"lng_group_call_is_recorded_video" = "The video stream is being recorded.";
"lng_group_call_is_recorded_channel" = "Live stream is being recorded.";
"lng_group_call_can_speak_here" = "You can now speak.";
"lng_group_call_can_speak" = "You can now speak in {chat}.";

Binary file not shown.

View File

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

View File

@@ -467,7 +467,7 @@ sendMessageUploadRoundAction#243e1c66 progress:int = SendMessageAction;
speakingInGroupCallAction#d92c2285 = SendMessageAction;
sendMessageHistoryImportAction#dbda9246 progress:int = SendMessageAction;
sendMessageChooseStickerAction#b05ac6b1 = SendMessageAction;
sendMessageEmojiInteraction#6a3233b6 emoticon:string interaction:DataJSON = SendMessageAction;
sendMessageEmojiInteraction#25972bcb emoticon:string msg_id:int interaction:DataJSON = SendMessageAction;
sendMessageEmojiInteractionSeen#b665902e emoticon:string = SendMessageAction;
contacts.found#b3134d9d my_results:Vector<Peer> results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found;
@@ -560,6 +560,7 @@ inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
stickerSet#d7df217a flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int count:int hash:int = StickerSet;
@@ -907,7 +908,6 @@ channelAdminLogEventActionExportedInviteRevoke#410a134e invite:ExportedChatInvit
channelAdminLogEventActionExportedInviteEdit#e90ebb59 prev_invite:ExportedChatInvite new_invite:ExportedChatInvite = ChannelAdminLogEventAction;
channelAdminLogEventActionParticipantVolume#3e7f6847 participant:GroupCallParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeTheme#fe69018d prev_value:string new_value:string = ChannelAdminLogEventAction;
channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent;

View File

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

View File

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

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,0,2,0
PRODUCTVERSION 3,0,2,0
FILEVERSION 3,1,3,0
PRODUCTVERSION 3,1,3,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "3.0.2.0"
VALUE "FileVersion", "3.1.3.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "3.0.2.0"
VALUE "ProductVersion", "3.1.3.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);
@@ -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);
@@ -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()

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

@@ -130,7 +130,6 @@ struct State {
not_null<HistoryItem*> item,
not_null<QWidget*> context) {
auto weak = QPointer<QWidget>(context.get());
const auto fullId = item->fullId();
const auto session = &item->history()->session();
return [=](auto consumer) {
if (!weak) {
@@ -203,7 +202,7 @@ bool UpdateUserpics(
}
auto &was = state->userpics;
auto now = std::vector<Userpic>();
for (const auto peer : peers) {
for (const auto &peer : peers) {
if (ranges::contains(now, peer, &Userpic::peer)) {
continue;
}

View File

@@ -46,6 +46,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"
@@ -3652,8 +3653,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 +3726,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(
@@ -4034,7 +4036,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);
@@ -4158,7 +4160,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 +4186,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 +4342,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 +4416,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());
@@ -4768,7 +4770,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

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

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

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

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

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

View File

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

View File

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

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

View File

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

View File

@@ -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(std::round(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"
@@ -667,7 +667,7 @@ void FieldAutocomplete::showAnimated() {
return;
}
if (_cache.isNull()) {
_stickersSeed = openssl::RandomValue<uint64>();
_stickersSeed = base::RandomValue<uint64>();
_scroll->show();
_cache = Ui::GrabWidget(this);
}

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

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

View File

@@ -47,6 +47,27 @@ std::map<int, const char*> BetaLogs() {
"from the context menu.\n"
"- Enable recording with video in live streams and video chats."
},
{
3000004,
"- Fix a crash when joining video chat or live broadcast.\n"
"- Add a \"Close to Taskbar\" option when tray icon is disabled "
"(Windows and Linux)."
},
{
3000005,
"- Add support for Emoji 13.1."
},
{
3001002,
"- Control video in fullscreen mode using arrows and numbers.\n"
"- Open locations in browser if default Bing Maps is not installed.\n"
"- Reconnect without timeout when network availability changes.\n"
"- Crash fixes."
}
};
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -75,6 +75,13 @@ public:
[[nodiscard]] rpl::producer<std::optional<ChatTheme>> themeForEmojiValue(
const QString &emoji);
[[nodiscard]] static bool TestingColors();
static void SetTestingColors(bool testing);
[[nodiscard]] QString prepareTestingLink(const CloudTheme &theme) const;
[[nodiscard]] std::optional<CloudTheme> updateThemeFromLink(
const QString &emoji,
const QMap<QString, QString> &params);
void applyUpdate(const MTPTheme &theme);
void resolve(

View File

@@ -60,7 +60,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_info.h"
#include "base/unixtime.h"
#include "base/call_delayed.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include "facades.h" // Notify::switchInlineBotButtonReceived
#include "app.h"
#include "styles/style_boxes.h" // st::backgroundSize
@@ -2427,7 +2427,7 @@ PhotoData *Session::photoFromWeb(
return nullptr;
}
return photo(
openssl::RandomValue<PhotoId>(),
base::RandomValue<PhotoId>(),
uint64(0),
QByteArray(),
base::unixtime::now(),
@@ -2692,7 +2692,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 +2714,7 @@ DocumentData *Session::documentFromWeb(
const ImageLocation &thumbnailLocation,
const ImageLocation &videoThumbnailLocation) {
const auto result = document(
openssl::RandomValue<DocumentId>(),
base::RandomValue<DocumentId>(),
uint64(0),
QByteArray(),
base::unixtime::now(),

View File

@@ -341,6 +341,9 @@ enum class MessageFlag : uint32 {
// Fake message for some UI element.
FakeHistoryItem = (1U << 27),
// Contact sign-up message, notification should be skipped for Silent.
IsContactSignUp = (1U << 28),
};
inline constexpr bool is_flag_type(MessageFlag) { return true; }
using MessageFlags = base::flags<MessageFlag>;

View File

@@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/mtproto_config.h"
#include "ui/toast/toast.h"
#include "ui/image/image_location_factory.h"
#include "base/openssl_help.h"
#include "base/unixtime.h"
#include "styles/style_chat_helpers.h"

View File

@@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Editor {
QImage ImageModified(QImage image, const PhotoModifications &mods) {
Expects(!image.isNull());
if (!mods) {
return image;
}

View File

@@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/mtproto_response.h"
#include "base/value_ordering.h"
#include "base/bytes.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include <set>
#include <deque>
@@ -1748,7 +1748,7 @@ auto ApiWrap::prepareFileProcess(
result->location = file.location;
result->size = file.size;
result->origin = origin;
result->randomId = openssl::RandomValue<uint64>();
result->randomId = base::RandomValue<uint64>();
return result;
}

View File

@@ -674,6 +674,9 @@ not_null<Ui::PathShiftGradient*> InnerWidget::elementPathShiftGradient() {
void InnerWidget::elementReplyTo(const FullMsgId &to) {
}
void InnerWidget::elementStartInteraction(not_null<const Element*> view) {
}
void InnerWidget::saveState(not_null<SectionMemento*> memento) {
memento->setFilter(std::move(_filter));
memento->setAdmins(std::move(_admins));

View File

@@ -137,6 +137,8 @@ public:
bool elementIsChatWide() override;
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
void elementReplyTo(const FullMsgId &to) override;
void elementStartInteraction(
not_null<const HistoryView::Element*> view) override;
~InnerWidget();

View File

@@ -1076,12 +1076,6 @@ void GenerateItems(
addSimpleServiceMessage(text);
};
auto createChangeTheme = [&](const MTPDchannelAdminLogEventActionChangeTheme &data) {
const auto was = qs(data.vprev_value());
const auto now = qs(data.vnew_value());
// #TODO themes
};
action.match([&](const MTPDchannelAdminLogEventActionChangeTitle &data) {
createChangeTitle(data);
}, [&](const MTPDchannelAdminLogEventActionChangeAbout &data) {
@@ -1146,8 +1140,6 @@ void GenerateItems(
createParticipantVolume(data);
}, [&](const MTPDchannelAdminLogEventActionChangeHistoryTTL &data) {
createChangeHistoryTTL(data);
}, [&](const MTPDchannelAdminLogEventActionChangeTheme &data) {
createChangeTheme(data);
});
}

View File

@@ -487,6 +487,19 @@ not_null<HistoryItem*> History::addNewItem(
} else if (!item->isHistoryEntry()) {
return item;
}
// In case we've loaded a new 'last' message
// and it is not in blocks and we think that
// we have all the messages till the bottom
// we should unload known history or mark
// currently loaded slice as not reaching bottom.
const auto shouldMarkBottomNotLoaded = loadedAtBottom()
&& !unread
&& !isEmpty();
if (shouldMarkBottomNotLoaded) {
setNotLoadedAtBottom();
}
if (!loadedAtBottom() || peer->migrateTo()) {
setLastMessage(item);
if (unread) {

View File

@@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/history_inner_widget.h"
#include <rpl/merge.h>
#include "core/file_utilities.h"
#include "core/crash_reports.h"
#include "core/click_handler_types.h"
@@ -22,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_service_message.h"
#include "history/view/history_view_cursor_state.h"
#include "history/view/history_view_context_menu.h"
#include "history/view/history_view_emoji_interactions.h"
#include "ui/chat/chat_theme.h"
#include "ui/chat/chat_style.h"
#include "ui/widgets/popup_menu.h"
@@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/confirm_box.h"
#include "boxes/sticker_set_box.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/emoji_interactions.h"
#include "history/history_widget.h"
#include "base/platform/base_platform_info.h"
#include "base/unixtime.h"
@@ -160,6 +161,8 @@ HistoryInner::HistoryInner(
, _controller(controller)
, _peer(history->peer)
, _history(history)
, _emojiInteractions(std::make_unique<HistoryView::EmojiInteractions>(
&controller->session()))
, _migrated(history->migrateFrom())
, _pathGradient(
HistoryView::MakePathShiftGradient(
@@ -195,6 +198,26 @@ HistoryInner::HistoryInner(
update();
}
}, lifetime());
using PlayRequest = ChatHelpers::EmojiInteractionPlayRequest;
_controller->emojiInteractions().playRequests(
) | rpl::filter([=](const PlayRequest &request) {
return (request.item->history() == _history)
&& _controller->widget()->isActive();
}) | rpl::start_with_next([=](PlayRequest &&request) {
if (const auto view = request.item->mainView()) {
_emojiInteractions->play(std::move(request), view);
}
}, lifetime());
_emojiInteractions->updateRequests(
) | rpl::start_with_next([=](QRect rect) {
update(rect.translated(0, _historyPaddingTop));
}, lifetime());
_emojiInteractions->playStarted(
) | rpl::start_with_next([=](QString &&emoji) {
_controller->emojiInteractions().playStarted(_peer, std::move(emoji));
}, lifetime());
session().data().itemRemoved(
) | rpl::start_with_next(
[this](auto item) { itemRemoved(item); },
@@ -834,6 +857,9 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
}
return true;
});
p.setOpacity(1.);
p.translate(0, _historyPaddingTop);
_emojiInteractions->paint(p);
}
}
}
@@ -2357,6 +2383,10 @@ void HistoryInner::visibleAreaUpdated(int top, int bottom) {
const auto till = _visibleAreaBottom + pages * visibleAreaHeight;
session().data().unloadHeavyViewParts(ElementDelegate(), from, till);
checkHistoryActivation();
_emojiInteractions->visibleAreaUpdated(
_visibleAreaTop - _historyPaddingTop,
_visibleAreaBottom - _historyPaddingTop);
}
bool HistoryInner::displayScrollDate() const {
@@ -2454,7 +2484,12 @@ void HistoryInner::updateSize() {
_botAbout->rect = QRect(descAtX, descAtY, _botAbout->width + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom());
}
_historyPaddingTop = newHistoryPaddingTop;
if (_historyPaddingTop != newHistoryPaddingTop) {
_historyPaddingTop = newHistoryPaddingTop;
_emojiInteractions->visibleAreaUpdated(
_visibleAreaTop - _historyPaddingTop,
_visibleAreaBottom - _historyPaddingTop);
}
int newHeight = _historyPaddingTop + itemsHeight + st::historyPaddingBottom;
if (width() != _scroll->width() || height() != newHeight) {
@@ -2689,6 +2724,10 @@ void HistoryInner::elementReplyTo(const FullMsgId &to) {
return _widget->replyToMessage(to);
}
void HistoryInner::elementStartInteraction(not_null<const Element*> view) {
_controller->emojiInteractions().startOutgoing(view);
}
auto HistoryInner::getSelectionState() const
-> HistoryView::TopBarWidget::SelectedState {
auto result = HistoryView::TopBarWidget::SelectedState {};
@@ -3601,6 +3640,11 @@ not_null<HistoryView::ElementDelegate*> HistoryInner::ElementDelegate() {
Instance->elementReplyTo(to);
}
}
void elementStartInteraction(not_null<const Element*> view) override {
if (Instance) {
Instance->elementStartInteraction(view);
}
}
};
static Result result;

View File

@@ -21,6 +21,7 @@ class CloudImageView;
namespace HistoryView {
class ElementDelegate;
class EmojiInteractions;
struct TextState;
struct StateRequest;
enum class CursorState : char;
@@ -115,6 +116,7 @@ public:
bool elementIsChatWide();
not_null<Ui::PathShiftGradient*> elementPathShiftGradient();
void elementReplyTo(const FullMsgId &to);
void elementStartInteraction(not_null<const Element*> view);
void updateBotInfo(bool recount = true);
@@ -353,6 +355,7 @@ private:
const not_null<Window::SessionController*> _controller;
const not_null<PeerData*> _peer;
const not_null<History*> _history;
const std::unique_ptr<HistoryView::EmojiInteractions> _emojiInteractions;
std::shared_ptr<Ui::ChatTheme> _theme;
History *_migrated = nullptr;

View File

@@ -459,6 +459,17 @@ bool HistoryItem::isScheduled() const {
&& (_flags & MessageFlag::IsOrWasScheduled);
}
bool HistoryItem::skipNotification() const {
if (isSilent() && (_flags & MessageFlag::IsContactSignUp)) {
return true;
} else if (const auto forwarded = Get<HistoryMessageForwarded>()) {
if (forwarded->imported) {
return true;
}
}
return false;
}
void HistoryItem::destroy() {
_history->destroyMessage(this);
}

View File

@@ -109,6 +109,7 @@ public:
[[nodiscard]] bool isAdminLogEntry() const;
[[nodiscard]] bool isFromScheduled() const;
[[nodiscard]] bool isScheduled() const;
[[nodiscard]] bool skipNotification() const;
void addLogEntryOriginal(
WebPageId localId,

View File

@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/history_message.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include "base/unixtime.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
@@ -279,10 +279,10 @@ void FastShareMessage(not_null<HistoryItem*> item) {
for (const auto &fullId : data->msgIds) {
msgIds.push_back(MTP_int(fullId.msg));
}
auto generateRandom = [&] {
const auto generateRandom = [&] {
auto result = QVector<MTPlong>(data->msgIds.size());
for (auto &value : result) {
value = openssl::RandomValue<MTPlong>();
value = base::RandomValue<MTPlong>();
}
return result;
};

View File

@@ -554,6 +554,8 @@ void HistoryService::applyAction(const MTPMessageAction &action) {
_flags |= MessageFlag::IsGroupEssential;
}, [&](const MTPDmessageActionChannelMigrateFrom &) {
_flags |= MessageFlag::IsGroupEssential;
}, [&](const MTPDmessageActionContactSignUp &) {
_flags |= MessageFlag::IsContactSignUp;
}, [](const auto &) {
});
}

View File

@@ -632,6 +632,7 @@ HistoryWidget::HistoryWidget(
| PeerUpdateFlag::Slowmode
| PeerUpdateFlag::BotStartToken
| PeerUpdateFlag::MessagesTTL
| PeerUpdateFlag::ChatThemeEmoji
) | rpl::filter([=](const Data::PeerUpdate &update) {
return (update.peer.get() == _peer);
}) | rpl::map([](const Data::PeerUpdate &update) {
@@ -675,6 +676,32 @@ HistoryWidget::HistoryWidget(
if (flags & PeerUpdateFlag::MessagesTTL) {
checkMessagesTTL();
}
if ((flags & PeerUpdateFlag::ChatThemeEmoji) && _list) {
const auto emoji = _peer->themeEmoji();
if (Data::CloudThemes::TestingColors() && !emoji.isEmpty()) {
_peer->owner().cloudThemes().themeForEmojiValue(
emoji
) | rpl::filter_optional(
) | rpl::take(
1
) | rpl::start_with_next([=](const Data::ChatTheme &theme) {
auto text = QStringList();
const auto push = [&](QString label, const auto &theme) {
using namespace Data;
const auto &themes = _peer->owner().cloudThemes();
const auto l = themes.prepareTestingLink(theme);
if (!l.isEmpty()) {
text.push_back(label + ": " + l);
}
};
push("Light", theme.light);
push("Dark", theme.dark);
if (!text.isEmpty()) {
_field->setText(text.join("\n\n"));
}
}, _list->lifetime());
}
}
}, lifetime());
rpl::merge(
@@ -1384,15 +1411,16 @@ bool HistoryWidget::updateStickersByEmoji() {
}
void HistoryWidget::fieldChanged() {
const auto typing = (_history
&& !_inlineBot
&& !_editMsgId
&& (_textUpdateEvents & TextUpdateEvent::SendTyping));
const auto updateTyping = (_textUpdateEvents & TextUpdateEvent::SendTyping);
InvokeQueued(this, [=] {
updateInlineBotQuery();
const auto choosingSticker = updateStickersByEmoji();
if (!choosingSticker && typing) {
if (_history
&& !_inlineBot
&& !_editMsgId
&& !choosingSticker
&& updateTyping) {
session().sendProgressManager().update(
_history,
Api::SendProgressType::Typing);

View File

@@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_send_progress.h"
#include "base/event_filter.h"
#include "base/openssl_help.h"
#include "base/random.h"
#include "base/unixtime.h"
#include "boxes/confirm_box.h"
#include "core/application.h"
@@ -98,7 +98,7 @@ enum class FilterType {
[[nodiscard]] not_null<DocumentData*> DummyDocument(
not_null<Data::Session*> owner) {
return owner->document(
openssl::RandomValue<DocumentId>(),
base::RandomValue<DocumentId>(),
uint64(0),
QByteArray(),
base::unixtime::now(),

View File

@@ -67,7 +67,8 @@ std::unique_ptr<Ui::PathShiftGradient> MakePathShiftGradient(
return std::make_unique<Ui::PathShiftGradient>(
st->msgServiceBg(),
st->msgServiceBgSelected(),
std::move(update));
std::move(update),
st->paletteChanged());
}
SimpleElementDelegate::SimpleElementDelegate(
@@ -176,6 +177,11 @@ auto SimpleElementDelegate::elementPathShiftGradient()
void SimpleElementDelegate::elementReplyTo(const FullMsgId &to) {
}
void SimpleElementDelegate::elementStartInteraction(
not_null<const Element*> view) {
}
TextSelection UnshiftItemSelection(
TextSelection selection,
uint16 byLength) {
@@ -218,24 +224,6 @@ QString DateTooltipText(not_null<Element*> view) {
tr::now,
lt_date,
base::unixtime::parse(forwarded->originalDate).toString(format));
if (const auto media = view->media()) {
if (media->hidesForwardedInfo()) {
const auto from = forwarded->originalSender
? forwarded->originalSender->shortName()
: forwarded->hiddenSenderInfo->firstName;
if (forwarded->imported) {
dateText += '\n' + tr::lng_signed_author(
tr::now,
lt_user,
from);
} else {
dateText += '\n' + tr::lng_forwarded(
tr::now,
lt_user,
from);
}
}
}
if (forwarded->imported) {
dateText = tr::lng_forwarded_imported(tr::now)
+ "\n\n" + dateText;

View File

@@ -90,6 +90,7 @@ public:
virtual bool elementIsChatWide() = 0;
virtual not_null<Ui::PathShiftGradient*> elementPathShiftGradient() = 0;
virtual void elementReplyTo(const FullMsgId &to) = 0;
virtual void elementStartInteraction(not_null<const Element*> view) = 0;
virtual ~ElementDelegate() {
}
@@ -146,6 +147,7 @@ public:
bool elementIsChatWide() override;
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
void elementReplyTo(const FullMsgId &to) override;
void elementStartInteraction(not_null<const Element*> view) override;
protected:
[[nodiscard]] not_null<Window::SessionController*> controller() const {

View File

@@ -0,0 +1,275 @@
/*
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 "history/view/history_view_emoji_interactions.h"
#include "history/view/history_view_element.h"
#include "history/view/media/history_view_sticker.h"
#include "history/history.h"
#include "chat_helpers/emoji_interactions.h"
#include "chat_helpers/stickers_lottie.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "lottie/lottie_common.h"
#include "lottie/lottie_single_player.h"
#include "base/random.h"
#include "styles/style_chat.h"
namespace HistoryView {
namespace {
constexpr auto kSizeMultiplier = 3;
constexpr auto kCachesCount = 4;
constexpr auto kMaxPlays = 5;
constexpr auto kMaxPlaysWithSmallDelay = 3;
constexpr auto kSmallDelay = crl::time(200);
constexpr auto kDropDelayedAfterDelay = crl::time(2000);
[[nodiscard]] QPoint GenerateRandomShift(QSize emoji) {
// Random shift in [-0.08 ... 0.08] of animated emoji size.
const auto maxShift = emoji * 2 / 25;
return {
base::RandomIndex(maxShift.width() * 2 + 1) - maxShift.width(),
base::RandomIndex(maxShift.height() * 2 + 1) - maxShift.height(),
};
}
} // namespace
EmojiInteractions::EmojiInteractions(not_null<Main::Session*> session)
: _session(session) {
_session->data().viewRemoved(
) | rpl::filter([=] {
return !_plays.empty();
}) | rpl::start_with_next([=](not_null<const Element*> view) {
_plays.erase(ranges::remove(_plays, view, &Play::view), end(_plays));
}, _lifetime);
_emojiSize = Sticker::EmojiSize();
}
EmojiInteractions::~EmojiInteractions() = default;
void EmojiInteractions::play(
ChatHelpers::EmojiInteractionPlayRequest request,
not_null<Element*> view) {
if (!view->media()) {
// Large emoji may be disabled.
return;
} else if (_plays.empty()) {
play(
std::move(request.emoticon),
view,
std::move(request.media),
request.incoming);
} else {
const auto now = crl::now();
_delayed.push_back({
request.emoticon,
view,
std::move(request.media),
now,
request.incoming,
});
checkDelayed();
}
}
void EmojiInteractions::play(
QString emoticon,
not_null<Element*> view,
std::shared_ptr<Data::DocumentMedia> media,
bool incoming) {
const auto top = view->block()->y() + view->y();
const auto bottom = top + view->height();
if (_visibleTop >= bottom
|| _visibleBottom <= top
|| _visibleTop == _visibleBottom) {
return;
}
auto lottie = preparePlayer(media.get());
const auto shift = GenerateRandomShift(_emojiSize);
lottie->updates(
) | rpl::start_with_next([=](Lottie::Update update) {
v::match(update.data, [&](const Lottie::Information &information) {
}, [&](const Lottie::DisplayFrameRequest &request) {
const auto rect = computeRect(view).translated(shift);
if (rect.y() + rect.height() >= _visibleTop
&& rect.y() <= _visibleBottom) {
_updateRequests.fire_copy(rect);
}
});
}, lottie->lifetime());
_plays.push_back({
.view = view,
.lottie = std::move(lottie),
.shift = shift,
});
if (incoming) {
_playStarted.fire(std::move(emoticon));
}
if (const auto media = view->media()) {
media->stickerClearLoopPlayed();
}
}
std::unique_ptr<Lottie::SinglePlayer> EmojiInteractions::preparePlayer(
not_null<Data::DocumentMedia*> media) {
// Shortened copy from stickers_lottie module.
const auto document = media->owner();
const auto baseKey = document->bigFileBaseCacheKey();
const auto tag = uint8(0);
const auto keyShift = ((tag << 4) & 0xF0)
| (uint8(ChatHelpers::StickerLottieSize::EmojiInteraction) & 0x0F);
const auto key = Storage::Cache::Key{
baseKey.high,
baseKey.low + keyShift
};
const auto get = [=](int i, FnMut<void(QByteArray &&cached)> handler) {
document->owner().cacheBigFile().get(
{ key.high, key.low + i },
std::move(handler));
};
const auto weak = base::make_weak(&document->session());
const auto put = [=](int i, QByteArray &&cached) {
crl::on_main(weak, [=, data = std::move(cached)]() mutable {
weak->data().cacheBigFile().put(
{ key.high, key.low + i },
std::move(data));
});
};
const auto data = media->bytes();
const auto filepath = document->filepath();
const auto request = Lottie::FrameRequest{
_emojiSize * kSizeMultiplier * style::DevicePixelRatio(),
};
auto &weakProvider = _sharedProviders[document];
auto shared = [&] {
if (const auto result = weakProvider.lock()) {
return result;
}
const auto result = Lottie::SinglePlayer::SharedProvider(
kCachesCount,
get,
put,
Lottie::ReadContent(data, filepath),
request,
Lottie::Quality::High);
weakProvider = result;
return result;
}();
return std::make_unique<Lottie::SinglePlayer>(std::move(shared), request);
}
void EmojiInteractions::visibleAreaUpdated(
int visibleTop,
int visibleBottom) {
_visibleTop = visibleTop;
_visibleBottom = visibleBottom;
}
QRect EmojiInteractions::computeRect(not_null<Element*> view) const {
const auto fullWidth = view->width();
const auto shift = (_emojiSize.width() * kSizeMultiplier) / 40;
const auto skip = (view->hasFromPhoto() ? st::msgPhotoSkip : 0)
+ st::msgMargin.left();
const auto rightAligned = view->hasOutLayout()
&& !view->delegate()->elementIsChatWide();
const auto left = rightAligned
? (fullWidth - skip + shift - _emojiSize.width() * kSizeMultiplier)
: (skip - shift);
const auto viewTop = view->block()->y() + view->y() + view->marginTop();
const auto top = viewTop - _emojiSize.height();
return QRect(QPoint(left, top), _emojiSize * kSizeMultiplier);
}
void EmojiInteractions::paint(QPainter &p) {
const auto factor = style::DevicePixelRatio();
for (auto &play : _plays) {
if (!play.lottie->ready()) {
continue;
}
auto request = Lottie::FrameRequest();
request.box = _emojiSize * kSizeMultiplier * factor;
const auto rightAligned = play.view->hasOutLayout()
&& !play.view->delegate()->elementIsChatWide();
if (!rightAligned) {
request.mirrorHorizontal = true;
}
const auto frame = play.lottie->frameInfo(request);
play.frame = frame.index;
if (!play.framesCount) {
const auto &information = play.lottie->information();
play.framesCount = information.framesCount;
play.frameRate = information.frameRate;
}
if (play.frame + 1 == play.framesCount) {
play.finished = true;
}
const auto rect = computeRect(play.view);
p.drawImage(
QRect(rect.topLeft() + play.shift, frame.image.size() / factor),
frame.image);
play.lottie->markFrameShown();
}
_plays.erase(ranges::remove(_plays, true, &Play::finished), end(_plays));
checkDelayed();
}
void EmojiInteractions::checkDelayed() {
if (_delayed.empty() || _plays.size() >= kMaxPlays) {
return;
}
auto withTooLittleDelay = false;
auto withHalfPlayed = false;
for (const auto &play : _plays) {
if (!play.framesCount
|| !play.frameRate
|| !play.frame
|| (play.frame * crl::time(1000)
< kSmallDelay * play.frameRate)) {
withTooLittleDelay = true;
break;
} else if (play.frame * 2 > play.framesCount) {
withHalfPlayed = true;
}
}
if (withTooLittleDelay) {
return;
} else if (_plays.size() >= kMaxPlaysWithSmallDelay && !withHalfPlayed) {
return;
}
const auto now = crl::now();
const auto i = ranges::find_if(_delayed, [&](const Delayed &delayed) {
return (delayed.shouldHaveStartedAt + kDropDelayedAfterDelay > now);
});
if (i == end(_delayed)) {
_delayed.clear();
return;
}
auto good = std::move(*i);
_delayed.erase(begin(_delayed), i + 1);
play(
std::move(good.emoticon),
good.view,
std::move(good.media),
good.incoming);
}
rpl::producer<QRect> EmojiInteractions::updateRequests() const {
return _updateRequests.events();
}
rpl::producer<QString> EmojiInteractions::playStarted() const {
return _playStarted.events();
}
} // namespace HistoryView

View File

@@ -0,0 +1,93 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Data {
class DocumentMedia;
} // namespace Data
namespace ChatHelpers {
struct EmojiInteractionPlayRequest;
} // namespace ChatHelpers
namespace Lottie {
class SinglePlayer;
class FrameProvider;
} // namespace Lottie
namespace Main {
class Session;
} // namespace Main
namespace HistoryView {
class Element;
class EmojiInteractions final {
public:
explicit EmojiInteractions(not_null<Main::Session*> session);
~EmojiInteractions();
void play(
ChatHelpers::EmojiInteractionPlayRequest request,
not_null<Element*> view);
void visibleAreaUpdated(int visibleTop, int visibleBottom);
void paint(QPainter &p);
[[nodiscard]] rpl::producer<QRect> updateRequests() const;
[[nodiscard]] rpl::producer<QString> playStarted() const;
private:
struct Play {
not_null<Element*> view;
std::unique_ptr<Lottie::SinglePlayer> lottie;
QPoint shift;
int frame = 0;
int framesCount = 0;
int frameRate = 0;
bool finished = false;
};
struct Delayed {
QString emoticon;
not_null<Element*> view;
std::shared_ptr<Data::DocumentMedia> media;
crl::time shouldHaveStartedAt = 0;
bool incoming = false;
};
[[nodiscard]] QRect computeRect(not_null<Element*> view) const;
void play(
QString emoticon,
not_null<Element*> view,
std::shared_ptr<Data::DocumentMedia> media,
bool incoming);
void checkDelayed();
[[nodiscard]] std::unique_ptr<Lottie::SinglePlayer> preparePlayer(
not_null<Data::DocumentMedia*> media);
const not_null<Main::Session*> _session;
int _visibleTop = 0;
int _visibleBottom = 0;
QSize _emojiSize;
std::vector<Play> _plays;
std::vector<Delayed> _delayed;
rpl::event_stream<QRect> _updateRequests;
rpl::event_stream<QString> _playStarted;
base::flat_map<
not_null<DocumentData*>,
std::weak_ptr<Lottie::FrameProvider>> _sharedProviders;
rpl::lifetime _lifetime;
};
} // namespace HistoryView

View File

@@ -1391,6 +1391,9 @@ void ListWidget::elementReplyTo(const FullMsgId &to) {
replyToMessageRequestNotify(to);
}
void ListWidget::elementStartInteraction(not_null<const Element*> view) {
}
void ListWidget::saveState(not_null<ListMemento*> memento) {
memento->setAroundPosition(_aroundPosition);
auto state = countScrollState();

View File

@@ -260,6 +260,7 @@ public:
bool elementIsChatWide() override;
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
void elementReplyTo(const FullMsgId &to) override;
void elementStartInteraction(not_null<const Element*> view) override;
void setEmptyInfoWidget(base::unique_qptr<Ui::RpWidget> &&w);

View File

@@ -1421,6 +1421,7 @@ QPixmap RepliesWidget::grabForShowAnimation(const Window::SectionSlideParams &pa
_composeControls->showForGrab();
auto result = Ui::GrabWidget(this);
if (params.withTopBarShadow) _topBarShadow->show();
_rootView->hide();
return result;
}
@@ -1537,7 +1538,7 @@ void RepliesWidget::restoreState(not_null<RepliesMemento*> memento) {
? (areComments
? tr::lng_comments_header
: tr::lng_replies_header)(
lt_count,
lt_count_decimal,
rpl::single(count) | tr::to_count())
: (areComments
? tr::lng_comments_header_none
@@ -1706,6 +1707,7 @@ void RepliesWidget::showAnimatedHook(
void RepliesWidget::showFinishedHook() {
_topBar->setAnimatingMode(false);
_composeControls->showFinished();
_rootView->show();
// We should setup the drag area only after
// the section animation is finished,

View File

@@ -119,7 +119,7 @@ bool SendActionPainter::updateNeedsAnimating(
Type::ChooseSticker,
kStatusShowClientsideChooseSticker);
}, [&](const MTPDsendMessageEmojiInteraction &) {
// #TODO interaction
Unexpected("EmojiInteraction here.");
}, [&](const MTPDsendMessageEmojiInteractionSeen &) {
// #TODO interaction
}, [&](const MTPDsendMessageCancelAction &) {
@@ -309,7 +309,7 @@ bool SendActionPainter::updateNeedsAnimating(crl::time now, bool force) {
// FontData::spacew for more precise calculation.
const auto mf = QFontMetricsF(_st.font->f);
_spacesCount = std::round(
_sendActionAnimation.width()
_sendActionAnimation.widthNoMargins()
/ mf.horizontalAdvance(' '));
}
newTypingString = newTypingString.replace(

View File

@@ -29,6 +29,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/radial_animation.h"
#include "ui/toasts/common_toasts.h"
#include "ui/boxes/report_box.h" // Ui::ReportReason
#include "ui/text/text.h"
#include "ui/text/text_options.h"
#include "ui/special_buttons.h"
#include "ui/unread_badge.h"
#include "ui/ui_utility.h"
@@ -45,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "data/data_changes.h"
#include "data/data_send_action.h"
#include "chat_helpers/emoji_interactions.h"
#include "base/unixtime.h"
#include "support/support_helper.h"
#include "apiwrap.h"
@@ -54,6 +57,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_info.h"
namespace HistoryView {
namespace {
constexpr auto kEmojiInteractionSeenDuration = 3 * crl::time(1000);
} // namespace
struct TopBarWidget::EmojiInteractionSeenAnimation {
Ui::SendActionAnimation animation;
Ui::Animations::Basic scheduler;
Ui::Text::String text = { st::dialogsTextWidthMin };
crl::time till = 0;
};
TopBarWidget::TopBarWidget(
QWidget *parent,
@@ -402,6 +417,7 @@ void TopBarWidget::paintTopBar(Painter &p) {
return;
}
const auto now = crl::now();
const auto history = _activeChat.key.history();
const auto folder = _activeChat.key.folder();
if (folder
@@ -442,14 +458,14 @@ void TopBarWidget::paintTopBar(Painter &p) {
p.setFont(st::dialogsTextFont);
if (!paintConnectingState(p, nameleft, statustop, width())
&& !_sendAction->paint(
&& !paintSendAction(
p,
nameleft,
statustop,
availableWidth,
width(),
st::historyStatusFgTyping,
crl::now())) {
now)) {
p.setPen(st::historyStatusFg);
p.drawTextLeft(nameleft, statustop, width(), _customTitleText);
}
@@ -481,19 +497,48 @@ void TopBarWidget::paintTopBar(Painter &p) {
p.setFont(st::dialogsTextFont);
if (!paintConnectingState(p, nameleft, statustop, width())
&& !_sendAction->paint(
&& !paintSendAction(
p,
nameleft,
statustop,
availableWidth,
width(),
st::historyStatusFgTyping,
crl::now())) {
now)) {
paintStatus(p, nameleft, statustop, availableWidth, width());
}
}
}
bool TopBarWidget::paintSendAction(
Painter &p,
int x,
int y,
int availableWidth,
int outerWidth,
style::color fg,
crl::time now) {
const auto seen = _emojiInteractionSeen.get();
if (!seen || seen->till <= now) {
return _sendAction->paint(p, x, y, availableWidth, outerWidth, fg, now);
}
const auto animationWidth = seen->animation.width();
const auto extraAnimationWidth = animationWidth * 2;
seen->animation.paint(
p,
fg,
x,
y + st::normalFont->ascent,
outerWidth,
now);
x += animationWidth;
availableWidth -= extraAnimationWidth;
p.setPen(fg);
seen->text.drawElided(p, x, y, availableWidth);
return true;
}
bool TopBarWidget::paintConnectingState(
Painter &p,
int left,
@@ -597,6 +642,7 @@ void TopBarWidget::setActiveChat(
update();
if (peerChanged) {
_emojiInteractionSeen = nullptr;
_activeChatLifetime.destroy();
if (const auto history = _activeChat.key.history()) {
session().changes().peerFlagsValue(
@@ -615,6 +661,14 @@ void TopBarWidget::setActiveChat(
updateControlsVisibility();
updateControlsGeometry();
}, _activeChatLifetime);
using InteractionSeen = ChatHelpers::EmojiInteractionSeen;
_controller->emojiInteractions().seen(
) | rpl::filter([=](const InteractionSeen &seen) {
return (seen.peer == history->peer);
}) | rpl::start_with_next([=](const InteractionSeen &seen) {
handleEmojiInteractionSeen(seen.emoticon);
}, lifetime());
}
}
updateUnreadBadge();
@@ -628,6 +682,41 @@ void TopBarWidget::setActiveChat(
refreshUnreadBadge();
}
void TopBarWidget::handleEmojiInteractionSeen(const QString &emoticon) {
auto seen = _emojiInteractionSeen.get();
if (!seen) {
_emojiInteractionSeen
= std::make_unique<EmojiInteractionSeenAnimation>();
seen = _emojiInteractionSeen.get();
seen->animation.start(Ui::SendActionAnimation::Type::ChooseSticker);
seen->scheduler.init([=] {
if (seen->till <= crl::now()) {
crl::on_main(this, [=] {
if (_emojiInteractionSeen
&& _emojiInteractionSeen->till <= crl::now()) {
_emojiInteractionSeen = nullptr;
update();
}
});
} else {
const auto skip = st::topBarArrowPadding.bottom();
update(
_leftTaken,
st::topBarHeight - skip - st::dialogsTextFont->height,
seen->animation.width(),
st::dialogsTextFont->height);
}
});
seen->scheduler.start();
}
seen->till = crl::now() + kEmojiInteractionSeenDuration;
seen->text.setText(
st::dialogsTextStyle,
tr::lng_user_action_watching_animations(tr::now, lt_emoji, emoticon),
Ui::NameTextOptions());
update();
}
void TopBarWidget::setCustomTitle(const QString &title) {
if (_customTitleText != title) {
_customTitleText = title;

View File

@@ -95,6 +95,8 @@ protected:
int resizeGetHeight(int newWidth) override;
private:
struct EmojiInteractionSeenAnimation;
void refreshInfoButton();
void refreshLang();
void updateSearchVisibility();
@@ -109,6 +111,16 @@ private:
void showMenu();
void toggleInfoSection();
void handleEmojiInteractionSeen(const QString &emoticon);
bool paintSendAction(
Painter &p,
int x,
int y,
int availableWidth,
int outerWidth,
style::color fg,
crl::time now);
void updateConnectingState();
void updateAdaptiveLayout();
int countSelectedButtonsTop(float64 selectedShown);
@@ -140,6 +152,7 @@ private:
const not_null<Window::SessionController*> _controller;
ActiveChat _activeChat;
QString _customTitleText;
std::unique_ptr<EmojiInteractionSeenAnimation> _emojiInteractionSeen;
rpl::lifetime _activeChatLifetime;
int _selectedCount = 0;

View File

@@ -46,9 +46,7 @@ Dice::Dice(not_null<Element*> parent, not_null<Data::MediaDice*> dice)
Dice::~Dice() = default;
QSize Dice::size() {
return _start
? _start->size()
: Sticker::GetAnimatedEmojiSize(&_parent->history()->session());
return _start ? _start->size() : Sticker::EmojiSize();
}
ClickHandlerPtr Dice::link() {

View File

@@ -41,9 +41,6 @@ public:
_end->unloadHeavyPart();
}
}
bool hidesForwardedInfo() override {
return false;
}
private:
const not_null<Element*> _parent;

View File

@@ -224,12 +224,16 @@ void Game::draw(Painter &p, const PaintContext &context) const {
auto lineHeight = unitedLineHeight();
if (_titleLines) {
p.setPen(semibold);
p.setTextPalette(stm->semiboldPalette);
auto endskip = 0;
if (_title.hasSkipBlock()) {
endskip = _parent->skipBlockWidth();
}
_title.drawLeftElided(p, padding.left(), tshift, paintw, width(), _titleLines, style::al_left, 0, -1, endskip, false, context.selection);
tshift += _titleLines * lineHeight;
p.setTextPalette(stm->textPalette);
}
if (_descriptionLines) {
p.setPen(stm->historyTextFg);

View File

@@ -233,10 +233,6 @@ public:
return false;
}
[[nodiscard]] virtual bool hidesForwardedInfo() const {
return false;
}
// Sometimes webpages can force the bubble to fit their size instead of
// allowing message text to be as wide as possible (like wallpapers).
[[nodiscard]] virtual bool enforceBubbleWidth() const {

View File

@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_media_unwrapped.h"
#include "history/view/media/history_view_media_common.h"
#include "history/view/media/history_view_sticker.h"
#include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h"
#include "history/history_item.h"
@@ -42,7 +43,7 @@ QSize UnwrappedMedia::countOptimalSize() {
_content->refreshLink();
_contentSize = NonEmptySize(DownscaledSize(
_content->size(),
{ st::maxStickerSize, st::maxStickerSize }));
Sticker::Size()));
auto maxWidth = _contentSize.width();
const auto minimal = st::largeEmojiSize + 2 * st::largeEmojiOutline;
auto minHeight = std::max(_contentSize.height(), minimal);
@@ -461,9 +462,7 @@ int UnwrappedMedia::additionalWidth(
auto UnwrappedMedia::getDisplayedForwardedInfo() const
-> const HistoryMessageForwarded * {
return _content->hidesForwardedInfo()
? nullptr
: _parent->data()->Get<HistoryMessageForwarded>();
return _parent->data()->Get<HistoryMessageForwarded>();
}
} // namespace HistoryView

View File

@@ -47,9 +47,6 @@ public:
}
virtual void refreshLink() {
}
[[nodiscard]] virtual bool hidesForwardedInfo() {
return true;
}
[[nodiscard]] virtual bool alwaysShowOutTimestamp() {
return false;
}
@@ -84,9 +81,6 @@ public:
bool customInfoLayout() const override {
return true;
}
bool hidesForwardedInfo() const override {
return _content->hidesForwardedInfo();
}
void stickerClearLoopPlayed() override {
_content->stickerClearLoopPlayed();
}

View File

@@ -1207,31 +1207,67 @@ void Poll::paintFilling(
top += st::historyPollAnswerPadding.top();
PainterHighQualityEnabler hq(p);
p.setPen(Qt::NoPen);
const auto thickness = st::historyPollFillingHeight;
const auto max = awidth - st::historyPollFillingRight;
const auto size = anim::interpolate(st::historyPollFillingMin, max, filling);
const auto radius = st::historyPollFillingRadius;
const auto ftop = bottom - st::historyPollFillingBottom - thickness;
if (chosen && !correct) {
p.setBrush(st->boxTextFgError());
} else if (chosen && correct && _poll->quiz() && !context.outbg) {
p.setBrush(st->boxTextFgGood());
} else {
p.setBrush(stm->msgWaveformActive);
}
enum class Style {
Incorrect,
Correct,
Default,
};
const auto style = [&] {
if (chosen && !correct) {
return Style::Incorrect;
} else if (chosen && correct && _poll->quiz() && !context.outbg) {
return Style::Correct;
} else {
return Style::Default;
}
}();
auto barleft = aleft;
auto barwidth = size;
const auto &color = (style == Style::Incorrect)
? st->boxTextFgError()
: (style == Style::Correct)
? st->boxTextFgGood()
: stm->msgFileBg;
p.setPen(Qt::NoPen);
p.setBrush(color);
PainterHighQualityEnabler hq(p);
if (chosen || correct) {
const auto &icon = (chosen && !correct)
const auto &icon = (style == Style::Incorrect)
? st->historyPollChoiceWrong()
: st->historyPollChoiceRight();
: (style == Style::Correct)
? st->historyPollChoiceRight()
: stm->historyPollChoiceRight;
const auto cleft = aleft - st::historyPollPercentSkip - icon.width();
const auto ctop = ftop - (icon.height() - thickness) / 2;
p.drawEllipse(cleft, ctop, icon.width(), icon.height());
icon.paint(p, cleft, ctop, width);
const auto paintContent = [&](Painter &p) {
icon.paint(p, cleft, ctop, width);
};
if (style == Style::Default && usesBubblePattern(context)) {
const auto add = st::lineWidth * 2;
const auto target = QRect(
cleft,
ctop,
icon.width(),
icon.height()
).marginsAdded({ add, add, add, add });
Ui::PaintPatternBubblePart(
p,
context.viewport,
context.bubblesPattern->pixmap,
target,
paintContent,
_fillingIconCache);
} else {
paintContent(p);
}
//barleft += icon.width() - radius;
//barwidth -= icon.width() - radius;
}

View File

@@ -214,6 +214,7 @@ private:
Ui::Animations::Simple _wrongAnswerAnimation;
mutable QPoint _lastLinkPoint;
mutable QImage _userpicCircleCache;
mutable QImage _fillingIconCache;
mutable std::unique_ptr<CloseInformation> _close;

View File

@@ -139,9 +139,7 @@ bool SlotMachine::isEndResolved() const {
}
QSize SlotMachine::size() {
return _pull
? _pull->size()
: Sticker::GetAnimatedEmojiSize(&_parent->history()->session());
return _pull ? _pull->size() : Sticker::EmojiSize();
}
ClickHandlerPtr SlotMachine::link() {

View File

@@ -54,9 +54,6 @@ public:
}
}
}
bool hidesForwardedInfo() override {
return false;
}
private:
void resolveStarts(bool initSize = false);

View File

@@ -37,11 +37,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace HistoryView {
namespace {
[[nodiscard]] double GetEmojiStickerZoom(not_null<Main::Session*> session) {
return session->account().appConfig().get<double>(
"emojies_animated_zoom",
0.625);
}
constexpr auto kMaxSizeFixed = 512;
constexpr auto kMaxEmojiSizeFixed = 256;
[[nodiscard]] QImage CacheDiceImage(
const QString &emoji,
@@ -97,16 +94,13 @@ bool Sticker::isEmojiSticker() const {
}
void Sticker::initSize() {
_size = _data->dimensions;
if (isEmojiSticker() || _diceIndex >= 0) {
_size = GetAnimatedEmojiSize(&_data->session(), _size);
_size = Sticker::EmojiSize();
if (_diceIndex > 0) {
[[maybe_unused]] bool result = readyToDrawLottie();
}
} else {
_size = DownscaledSize(
_size,
{ st::maxStickerSize, st::maxStickerSize });
_size = DownscaledSize(_data->dimensions, Sticker::Size());
}
}
@@ -135,18 +129,14 @@ bool Sticker::readyToDrawLottie() {
return (_lottie && _lottie->ready());
}
QSize Sticker::GetAnimatedEmojiSize(not_null<Main::Session*> session) {
return GetAnimatedEmojiSize(session, { 512, 512 });
QSize Sticker::Size() {
const auto side = std::min(st::maxStickerSize, kMaxSizeFixed);
return { side, side };
}
QSize Sticker::GetAnimatedEmojiSize(
not_null<Main::Session*> session,
QSize documentSize) {
const auto zoom = GetEmojiStickerZoom(session);
const auto convert = [&](int size) {
return int(size * st::maxStickerSize * zoom / kStickerSideSize);
};
return { convert(documentSize.width()), convert(documentSize.height()) };
QSize Sticker::EmojiSize() {
const auto side = std::min(st::maxAnimatedEmojiSize, kMaxEmojiSizeFixed);
return { side, side };
}
void Sticker::draw(
@@ -295,13 +285,9 @@ void Sticker::refreshLink() {
if (isEmojiSticker()) {
const auto weak = base::make_weak(this);
_link = std::make_shared<LambdaClickHandler>([weak] {
const auto that = weak.get();
if (!that) {
return;
if (const auto that = weak.get()) {
that->emojiStickerClicked();
}
that->_lottieOncePlayed = false;
that->_parent->history()->owner().requestViewRepaint(
that->_parent);
});
} else if (sticker && sticker->set) {
_link = std::make_shared<LambdaClickHandler>([document = _data](ClickContext context) {
@@ -327,6 +313,14 @@ void Sticker::refreshLink() {
}
}
void Sticker::emojiStickerClicked() {
if (_lottie) {
_parent->delegate()->elementStartInteraction(_parent);
}
_lottieOncePlayed = false;
_parent->history()->owner().requestViewRepaint(_parent);
}
void Sticker::ensureDataMediaCreated() const {
if (_dataMedia) {
return;

View File

@@ -78,11 +78,8 @@ public:
}
[[nodiscard]] bool readyToDrawLottie();
[[nodiscard]] static QSize GetAnimatedEmojiSize(
not_null<Main::Session*> session);
[[nodiscard]] static QSize GetAnimatedEmojiSize(
not_null<Main::Session*> session,
QSize documentSize);
[[nodiscard]] static QSize Size();
[[nodiscard]] static QSize EmojiSize();
private:
[[nodiscard]] bool isEmojiSticker() const;
@@ -97,6 +94,7 @@ private:
void setupLottie();
void lottieCreated();
void unloadLottie();
void emojiStickerClicked();
const not_null<Element*> _parent;
const not_null<DocumentData*> _data;

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