Compare commits
308 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b44143f5b | ||
|
|
0f207faa3e | ||
|
|
28b43eff7c | ||
|
|
c257b75a66 | ||
|
|
893e14cc39 | ||
|
|
30d5b7fd66 | ||
|
|
2ef8136308 | ||
|
|
fccc93ca53 | ||
|
|
37d1940993 | ||
|
|
f25638f492 | ||
|
|
6e6f15e711 | ||
|
|
b38e72dcd9 | ||
|
|
9287af1752 | ||
|
|
254ca57bf3 | ||
|
|
52ef8e780a | ||
|
|
16a2d4ec96 | ||
|
|
3b50bc71b3 | ||
|
|
bb31357c58 | ||
|
|
66afcbdae8 | ||
|
|
5a28e69f1a | ||
|
|
ce78074df7 | ||
|
|
973f91b5e4 | ||
|
|
d2246337a2 | ||
|
|
c7f11eb05a | ||
|
|
758219265a | ||
|
|
d8d9441731 | ||
|
|
098e797045 | ||
|
|
69f8cb5951 | ||
|
|
ca86dce760 | ||
|
|
5cf0b6b50e | ||
|
|
7a139ecda7 | ||
|
|
e52fe9ddb0 | ||
|
|
cee2961632 | ||
|
|
5933535c9b | ||
|
|
a3b91da66c | ||
|
|
34d9a21aae | ||
|
|
233c6b18ed | ||
|
|
4274f9d3f3 | ||
|
|
c8dd94601b | ||
|
|
382dab4ecb | ||
|
|
bdf67645bb | ||
|
|
5c29cc59c8 | ||
|
|
ca9caa36da | ||
|
|
0b5f05c7d4 | ||
|
|
676e85983d | ||
|
|
7638f4cc3d | ||
|
|
e2e55312b8 | ||
|
|
c51a8816eb | ||
|
|
175914f02b | ||
|
|
fbd6b5b640 | ||
|
|
1b11731d6b | ||
|
|
8ecb49f132 | ||
|
|
0ae537478f | ||
|
|
4aa432ecbe | ||
|
|
d5a0f4890d | ||
|
|
5e255e56eb | ||
|
|
a4d7309209 | ||
|
|
a30d0eccda | ||
|
|
a4f4e4564a | ||
|
|
bfe7683cdb | ||
|
|
fbc600a978 | ||
|
|
70eb452a09 | ||
|
|
9f7c74ae72 | ||
|
|
65a3cf136b | ||
|
|
2d86ec1e84 | ||
|
|
fdef19a009 | ||
|
|
26df482b54 | ||
|
|
ee5b7a5100 | ||
|
|
bd67bc4433 | ||
|
|
9d582040e6 | ||
|
|
f3bda59019 | ||
|
|
0d72d47318 | ||
|
|
29646707a1 | ||
|
|
e6b9a07163 | ||
|
|
4b297bfa09 | ||
|
|
00e785a3af | ||
|
|
78e6b3e13f | ||
|
|
ad84750130 | ||
|
|
686310489b | ||
|
|
84c5310262 | ||
|
|
f53397e26a | ||
|
|
2a8a74b5b1 | ||
|
|
9392550c01 | ||
|
|
d2565dc944 | ||
|
|
22f68b430d | ||
|
|
3962e5a680 | ||
|
|
a1c7a48958 | ||
|
|
85286684e3 | ||
|
|
c2712b0104 | ||
|
|
1cb5ef7476 | ||
|
|
9f0b42bbbd | ||
|
|
634687881a | ||
|
|
878b4bb5af | ||
|
|
452257dcd5 | ||
|
|
4e6d8f06d9 | ||
|
|
fd417024fb | ||
|
|
18c4d210e5 | ||
|
|
ead40c759e | ||
|
|
be9aa3a097 | ||
|
|
bdcb146d06 | ||
|
|
70115a24bb | ||
|
|
ea37e83b13 | ||
|
|
931c17418d | ||
|
|
5a47ed268c | ||
|
|
d63ebbe62c | ||
|
|
cb4fce251e | ||
|
|
4aa8a41119 | ||
|
|
13cba72945 | ||
|
|
cf63b0138e | ||
|
|
3cbe0aae4a | ||
|
|
5ab8e68366 | ||
|
|
1d345299f5 | ||
|
|
fc50d5c30f | ||
|
|
4e3c1460f6 | ||
|
|
5bc954396c | ||
|
|
b24290b019 | ||
|
|
23cce64d00 | ||
|
|
73690d14f7 | ||
|
|
2a5698cf34 | ||
|
|
fd64718502 | ||
|
|
941126ad69 | ||
|
|
0e8058adb1 | ||
|
|
01906c1161 | ||
|
|
9201cf24f1 | ||
|
|
d5a1c354d0 | ||
|
|
41ae1f56ed | ||
|
|
ed7212f864 | ||
|
|
8bcb784f12 | ||
|
|
431549c81a | ||
|
|
12110e17a2 | ||
|
|
db8338156a | ||
|
|
de8b09d7fc | ||
|
|
fddbce5dce | ||
|
|
081817f62a | ||
|
|
8efbd7a1cb | ||
|
|
bce310d5c8 | ||
|
|
ed9ecbd235 | ||
|
|
1e756dd380 | ||
|
|
b9b6226692 | ||
|
|
82d73e2396 | ||
|
|
cd5a6025f6 | ||
|
|
b6c679449e | ||
|
|
ac744b957a | ||
|
|
805a5d73b6 | ||
|
|
6aaf841a73 | ||
|
|
60e72768e1 | ||
|
|
94e8f2a791 | ||
|
|
1fb4a2f4ba | ||
|
|
28d68acfe6 | ||
|
|
d87a0a2d25 | ||
|
|
8e92778b62 | ||
|
|
f7e2c7977b | ||
|
|
4337f0b509 | ||
|
|
2b960a1f21 | ||
|
|
4b9648d8d9 | ||
|
|
62f9f3c94b | ||
|
|
e854f0b60c | ||
|
|
19f38f3c6f | ||
|
|
d56724f290 | ||
|
|
8abc35ca86 | ||
|
|
e135f8954f | ||
|
|
f5b59c9456 | ||
|
|
6471d43c71 | ||
|
|
563b8d1468 | ||
|
|
f41a3fe01f | ||
|
|
29c9266ef5 | ||
|
|
1a856e359f | ||
|
|
59099a8d46 | ||
|
|
98c6a3ff79 | ||
|
|
cccc2ce0f1 | ||
|
|
88b20f6700 | ||
|
|
3adbfb1fb5 | ||
|
|
0ee0ffa7f1 | ||
|
|
4c82620677 | ||
|
|
73294bfabf | ||
|
|
6c42095108 | ||
|
|
fbe93b0afc | ||
|
|
e173c727f7 | ||
|
|
85f56217a8 | ||
|
|
06564efe0e | ||
|
|
664ebe4ed0 | ||
|
|
cb3dece478 | ||
|
|
c86ca6a61a | ||
|
|
8e2240d9d9 | ||
|
|
28acaf06ad | ||
|
|
fe12c3639b | ||
|
|
0e2131e6eb | ||
|
|
98be0a69df | ||
|
|
d6aecdfe4b | ||
|
|
fa63a220fa | ||
|
|
2611899448 | ||
|
|
514ced1d8e | ||
|
|
1a69975131 | ||
|
|
27b284ef5b | ||
|
|
a546b3a9b6 | ||
|
|
28d9330969 | ||
|
|
8c3fa14a75 | ||
|
|
e58940a876 | ||
|
|
ae1da5baf4 | ||
|
|
2542ec5d93 | ||
|
|
dfe55b26a2 | ||
|
|
6de95fc4ed | ||
|
|
1e57563349 | ||
|
|
9f0b4bc799 | ||
|
|
ac8117a6d8 | ||
|
|
9ef0e5cf83 | ||
|
|
f433d6fbc9 | ||
|
|
e24b86d460 | ||
|
|
bfebb1339a | ||
|
|
49b59d73be | ||
|
|
d41961945d | ||
|
|
aa29d1c619 | ||
|
|
d6d76c8477 | ||
|
|
6057bb2b37 | ||
|
|
5aaf119b36 | ||
|
|
8f9123bb96 | ||
|
|
77b2076e0f | ||
|
|
7681f14a3c | ||
|
|
4ad51ffb42 | ||
|
|
0f3faf59ca | ||
|
|
d28ba4fad9 | ||
|
|
3f19dc0486 | ||
|
|
fc86bb7a5f | ||
|
|
0da515abc5 | ||
|
|
4c5c2aadc4 | ||
|
|
17f89ba1f9 | ||
|
|
a8de145e01 | ||
|
|
c1d20d16fd | ||
|
|
dc04a1afdb | ||
|
|
856f39c123 | ||
|
|
909f3bf011 | ||
|
|
19ab740da2 | ||
|
|
11ea5e61cf | ||
|
|
78897dd143 | ||
|
|
23a0413113 | ||
|
|
b5b5c28ac5 | ||
|
|
5652abfd49 | ||
|
|
324f2f68ba | ||
|
|
f8825e8135 | ||
|
|
4dbe5c0a0f | ||
|
|
2df6729f2d | ||
|
|
30d72f1d1d | ||
|
|
db0a31b87e | ||
|
|
4b5e2582d9 | ||
|
|
6a87fef851 | ||
|
|
1fbcec1d24 | ||
|
|
25d0123b9f | ||
|
|
69b9d404c0 | ||
|
|
0865776d9a | ||
|
|
32008161a0 | ||
|
|
36a8c49213 | ||
|
|
91fba41e2c | ||
|
|
7d2d0bdfa2 | ||
|
|
2bb8850e69 | ||
|
|
1056a5cc8e | ||
|
|
34d0dac351 | ||
|
|
f88eee8047 | ||
|
|
173a5046e8 | ||
|
|
44f6280d0a | ||
|
|
b7346c203a | ||
|
|
c3254a53bc | ||
|
|
5ea066e6a7 | ||
|
|
bc6556ebc4 | ||
|
|
4c6d33fd54 | ||
|
|
96b651c29b | ||
|
|
0ffda016da | ||
|
|
43a8733fc7 | ||
|
|
1e26c33b3d | ||
|
|
4ad70965e9 | ||
|
|
3a67e4f1f4 | ||
|
|
dcc326e17f | ||
|
|
f442d69cb6 | ||
|
|
17fa50bdff | ||
|
|
a04145d981 | ||
|
|
e8c9cb7d2a | ||
|
|
467a6c25a8 | ||
|
|
1852386ace | ||
|
|
a93340c2e4 | ||
|
|
4a0ee22670 | ||
|
|
87a042b029 | ||
|
|
960761ef37 | ||
|
|
b60e50df10 | ||
|
|
4696f731da | ||
|
|
0f9d83f34b | ||
|
|
cfc254bd90 | ||
|
|
bdee6e0155 | ||
|
|
ac6765ebdb | ||
|
|
df3ae2c5f8 | ||
|
|
1af4af6ff3 | ||
|
|
a92e9f7162 | ||
|
|
27d9fa6acb | ||
|
|
7d4362267a | ||
|
|
1ba52402b5 | ||
|
|
5d0a36e456 | ||
|
|
af6e15ed59 | ||
|
|
25216a140e | ||
|
|
65fc7d2b10 | ||
|
|
99da95880e | ||
|
|
12272a450f | ||
|
|
819020e515 | ||
|
|
acb1a7ccf2 | ||
|
|
596460172f | ||
|
|
baa99be6a5 | ||
|
|
f748de9dc4 | ||
|
|
85e2d54a05 | ||
|
|
f629bf76ff | ||
|
|
f6cb9072d2 | ||
|
|
497aa6dbc8 |
5
.github/workflows/docker.yml
vendored
@@ -28,6 +28,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt update
|
||||
curl -sSL https://install.python-poetry.org | python3 -
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin
|
||||
|
||||
- name: Free up some disk space.
|
||||
uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8
|
||||
@@ -40,6 +41,4 @@ jobs:
|
||||
|
||||
- name: Push the Docker image.
|
||||
if: ${{ github.ref_name == github.event.repository.default_branch }}
|
||||
run: |
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin
|
||||
docker push $IMAGE_TAG
|
||||
run: docker push $IMAGE_TAG
|
||||
|
||||
27
.github/workflows/linux.yml
vendored
@@ -43,15 +43,6 @@ jobs:
|
||||
linux:
|
||||
name: Rocky Linux 8
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/${{ github.repository }}/centos_env
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: scl enable gcc-toolset-12 -- bash --noprofile --norc -eo pipefail {0}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -75,12 +66,13 @@ jobs:
|
||||
|
||||
- name: First set up.
|
||||
run: |
|
||||
gcc --version
|
||||
ln -s /usr/src/Libraries
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin
|
||||
docker pull ghcr.io/$GITHUB_REPOSITORY/centos_env
|
||||
docker tag ghcr.io/$GITHUB_REPOSITORY/centos_env tdesktop:centos_env
|
||||
|
||||
- name: Telegram Desktop build.
|
||||
run: |
|
||||
cd $REPO_NAME/Telegram
|
||||
cd $REPO_NAME
|
||||
|
||||
DEFINE=""
|
||||
if [ -n "${{ matrix.defines }}" ]; then
|
||||
@@ -91,18 +83,21 @@ jobs:
|
||||
echo "ARTIFACT_NAME=Telegram" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
./configure.sh \
|
||||
docker run --rm \
|
||||
-v $PWD:/usr/src/tdesktop \
|
||||
-e DEBUG=1 \
|
||||
tdesktop:centos_env \
|
||||
/usr/src/tdesktop/Telegram/build/docker/centos_env/build.sh \
|
||||
-D CMAKE_C_FLAGS_DEBUG="" \
|
||||
-D CMAKE_CXX_FLAGS_DEBUG="" \
|
||||
-D CMAKE_C_FLAGS="-Werror" \
|
||||
-D CMAKE_CXX_FLAGS="-Werror" \
|
||||
-D CMAKE_EXE_LINKER_FLAGS="-s" \
|
||||
-D TDESKTOP_API_TEST=ON \
|
||||
-D DESKTOP_APP_DISABLE_AUTOUPDATE=OFF \
|
||||
-D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF \
|
||||
$DEFINE
|
||||
|
||||
cmake --build ../out --config Debug --parallel
|
||||
|
||||
- name: Check.
|
||||
run: |
|
||||
filePath="$REPO_NAME/out/Debug/Telegram"
|
||||
@@ -121,7 +116,7 @@ jobs:
|
||||
run: |
|
||||
cd $REPO_NAME/out/Debug
|
||||
mkdir artifact
|
||||
mv Telegram artifact/
|
||||
mv {Telegram,Updater} artifact/
|
||||
- uses: actions/upload-artifact@master
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
name: Upload artifact.
|
||||
|
||||
1
.github/workflows/mac.yml
vendored
@@ -115,6 +115,7 @@ jobs:
|
||||
-D CMAKE_CXX_FLAGS="-Werror" \
|
||||
-D CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=NO \
|
||||
-D TDESKTOP_API_TEST=ON \
|
||||
-D DESKTOP_APP_DISABLE_AUTOUPDATE=OFF \
|
||||
-D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF \
|
||||
$DEFINE
|
||||
|
||||
|
||||
50
.github/workflows/win.yml
vendored
@@ -47,6 +47,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [Win32, x64]
|
||||
generator: ["", "Ninja Multi-Config"]
|
||||
|
||||
env:
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
@@ -109,19 +110,42 @@ jobs:
|
||||
cd %TBUILD%
|
||||
%REPO_NAME%\Telegram\build\prepare\win.bat skip-release silent
|
||||
|
||||
- name: Read defines.
|
||||
- name: Read configuration matrix.
|
||||
shell: bash
|
||||
run: |
|
||||
ARTIFACT_NAME="Telegram"
|
||||
|
||||
ARCH=""
|
||||
if [ -n "${{ matrix.arch }}" ]; then
|
||||
case "${{ matrix.arch }}" in
|
||||
Win32) ARCH="x86";;
|
||||
*) ARCH="${{ matrix.arch }}";;
|
||||
esac
|
||||
echo "Architecture from matrix: $ARCH"
|
||||
ARTIFACT_NAME="${ARTIFACT_NAME}_${{ matrix.arch }}"
|
||||
fi
|
||||
|
||||
GENERATOR=""
|
||||
if [ -n "${{ matrix.generator }}" ]; then
|
||||
GENERATOR="-G \"${{ matrix.generator }}\""
|
||||
echo "Generator from matrix: $GENERATOR"
|
||||
ARTIFACT_NAME="${ARTIFACT_NAME}_${{ matrix.generator }}"
|
||||
fi
|
||||
echo "TDESKTOP_BUILD_GENERATOR=$GENERATOR" >> $GITHUB_ENV
|
||||
|
||||
[ -n "$GENERATOR" ] && ARCH=""
|
||||
echo "TDESKTOP_BUILD_ARCH=$ARCH" >> $GITHUB_ENV
|
||||
|
||||
DEFINE=""
|
||||
if [ -n "${{ matrix.defines }}" ]; then
|
||||
DEFINE="-D ${{ matrix.defines }}=ON"
|
||||
echo "Define from matrix: $DEFINE"
|
||||
echo "ARTIFACT_NAME=Telegram_${{ matrix.arch }}_${{ matrix.defines }}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "ARTIFACT_NAME=Telegram_${{ matrix.arch }}" >> $GITHUB_ENV
|
||||
ARTIFACT_NAME="${ARTIFACT_NAME}_${{ matrix.defines }}"
|
||||
fi
|
||||
echo "TDESKTOP_BUILD_DEFINE=$DEFINE" >> $GITHUB_ENV
|
||||
|
||||
echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> $GITHUB_ENV
|
||||
|
||||
API="-D TDESKTOP_API_TEST=ON"
|
||||
if [ $GITHUB_REF == 'refs/heads/nightly' ]; then
|
||||
echo "Use the open credentials."
|
||||
@@ -142,24 +166,26 @@ jobs:
|
||||
cd %TBUILD%\%REPO_NAME%\Telegram
|
||||
|
||||
call configure.bat ^
|
||||
${{ matrix.arch }} ^
|
||||
%TDESKTOP_BUILD_GENERATOR% ^
|
||||
%TDESKTOP_BUILD_ARCH% ^
|
||||
%TDESKTOP_BUILD_API% ^
|
||||
-D DESKTOP_APP_DISABLE_AUTOUPDATE=OFF ^
|
||||
-D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF ^
|
||||
-D DESKTOP_APP_NO_PDB=ON ^
|
||||
%TDESKTOP_BUILD_DEFINE% ^
|
||||
-DCMAKE_SYSTEM_VERSION=%SDK%
|
||||
%TDESKTOP_BUILD_DEFINE%
|
||||
|
||||
cd ..\out
|
||||
msbuild -m Telegram.sln /p:Configuration=Debug,Platform=${{ matrix.arch }},DebugSymbols=false,DebugType=none
|
||||
cmake --build ..\out --config Debug --parallel
|
||||
|
||||
- name: Move artifact.
|
||||
if: (env.UPLOAD_ARTIFACT == 'true') || ${{ github.ref == 'refs/heads/nightly' }}
|
||||
if: (env.UPLOAD_ARTIFACT == 'true') || (github.ref == 'refs/heads/nightly')
|
||||
run: |
|
||||
set OUT=%TBUILD%\%REPO_NAME%\out\Debug
|
||||
mkdir artifact
|
||||
move %TBUILD%\%REPO_NAME%\out\Debug\Telegram.exe artifact/
|
||||
move %OUT%\Telegram.exe artifact/
|
||||
move %OUT%\Updater.exe artifact/
|
||||
- uses: actions/upload-artifact@master
|
||||
name: Upload artifact.
|
||||
if: (env.UPLOAD_ARTIFACT == 'true') || ${{ github.ref == 'refs/heads/nightly' }}
|
||||
if: (env.UPLOAD_ARTIFACT == 'true') || (github.ref == 'refs/heads/nightly')
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: artifact\
|
||||
|
||||
3
.gitmodules
vendored
@@ -76,9 +76,6 @@
|
||||
[submodule "Telegram/lib_webview"]
|
||||
path = Telegram/lib_webview
|
||||
url = https://github.com/desktop-app/lib_webview.git
|
||||
[submodule "Telegram/ThirdParty/jemalloc"]
|
||||
path = Telegram/ThirdParty/jemalloc
|
||||
url = https://github.com/jemalloc/jemalloc
|
||||
[submodule "Telegram/ThirdParty/dispatch"]
|
||||
path = Telegram/ThirdParty/dispatch
|
||||
url = https://github.com/apple/swift-corelibs-libdispatch
|
||||
|
||||
@@ -10,8 +10,9 @@ if (APPLE)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
endif()
|
||||
cmake_policy(SET CMP0076 NEW)
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
if (POLICY CMP0149)
|
||||
cmake_policy(SET CMP0149 NEW)
|
||||
endif()
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
@@ -59,9 +60,9 @@ include(cmake/options.cmake)
|
||||
|
||||
if (NOT DESKTOP_APP_USE_PACKAGED)
|
||||
if (WIN32)
|
||||
set(qt_version 5.15.11)
|
||||
set(qt_version 5.15.12)
|
||||
elseif (APPLE)
|
||||
set(qt_version 6.2.6)
|
||||
set(qt_version 6.2.7)
|
||||
endif()
|
||||
endif()
|
||||
include(cmake/external/qt/package.cmake)
|
||||
|
||||
2
LEGAL
@@ -1,7 +1,7 @@
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
Copyright (c) 2014-2023 The Telegram Desktop Authors.
|
||||
Copyright (c) 2014-2024 The Telegram Desktop Authors.
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -130,6 +130,8 @@ PRIVATE
|
||||
api/api_messages_search.h
|
||||
api/api_messages_search_merged.cpp
|
||||
api/api_messages_search_merged.h
|
||||
api/api_peer_colors.cpp
|
||||
api/api_peer_colors.h
|
||||
api/api_peer_photo.cpp
|
||||
api/api_peer_photo.h
|
||||
api/api_polls.cpp
|
||||
@@ -380,16 +382,18 @@ PRIVATE
|
||||
chat_helpers/gifs_list_widget.h
|
||||
chat_helpers/message_field.cpp
|
||||
chat_helpers/message_field.h
|
||||
chat_helpers/share_message_phrase_factory.cpp
|
||||
chat_helpers/share_message_phrase_factory.h
|
||||
chat_helpers/spellchecker_common.cpp
|
||||
chat_helpers/spellchecker_common.h
|
||||
chat_helpers/stickers_dice_pack.cpp
|
||||
chat_helpers/stickers_dice_pack.h
|
||||
chat_helpers/stickers_emoji_image_loader.cpp
|
||||
chat_helpers/stickers_emoji_image_loader.h
|
||||
chat_helpers/stickers_emoji_pack.cpp
|
||||
chat_helpers/stickers_emoji_pack.h
|
||||
chat_helpers/stickers_gift_box_pack.cpp
|
||||
chat_helpers/stickers_gift_box_pack.h
|
||||
chat_helpers/stickers_dice_pack.cpp
|
||||
chat_helpers/stickers_dice_pack.h
|
||||
chat_helpers/stickers_list_footer.cpp
|
||||
chat_helpers/stickers_list_footer.h
|
||||
chat_helpers/stickers_list_widget.cpp
|
||||
@@ -546,6 +550,10 @@ PRIVATE
|
||||
data/data_replies_list.h
|
||||
data/data_reply_preview.cpp
|
||||
data/data_reply_preview.h
|
||||
data/data_saved_messages.cpp
|
||||
data/data_saved_messages.h
|
||||
data/data_saved_sublist.cpp
|
||||
data/data_saved_sublist.h
|
||||
data/data_search_controller.cpp
|
||||
data/data_search_controller.h
|
||||
data/data_send_action.cpp
|
||||
@@ -711,6 +719,8 @@ PRIVATE
|
||||
history/view/media/history_view_premium_gift.h
|
||||
history/view/media/history_view_service_box.cpp
|
||||
history/view/media/history_view_service_box.h
|
||||
history/view/media/history_view_similar_channels.cpp
|
||||
history/view/media/history_view_similar_channels.h
|
||||
history/view/media/history_view_slot_machine.cpp
|
||||
history/view/media/history_view_slot_machine.h
|
||||
history/view/media/history_view_sticker.cpp
|
||||
@@ -786,8 +796,12 @@ PRIVATE
|
||||
history/view/history_view_service_message.h
|
||||
history/view/history_view_spoiler_click_handler.cpp
|
||||
history/view/history_view_spoiler_click_handler.h
|
||||
history/view/history_view_sponsored_click_handler.cpp
|
||||
history/view/history_view_sponsored_click_handler.h
|
||||
history/view/history_view_sticker_toast.cpp
|
||||
history/view/history_view_sticker_toast.h
|
||||
history/view/history_view_sublist_section.cpp
|
||||
history/view/history_view_sublist_section.h
|
||||
history/view/history_view_transcribe_button.cpp
|
||||
history/view/history_view_transcribe_button.h
|
||||
history/view/history_view_translate_bar.cpp
|
||||
@@ -889,8 +903,12 @@ PRIVATE
|
||||
info/profile/info_profile_values.h
|
||||
info/profile/info_profile_widget.cpp
|
||||
info/profile/info_profile_widget.h
|
||||
info/saved/info_saved_sublists_widget.cpp
|
||||
info/saved/info_saved_sublists_widget.h
|
||||
info/settings/info_settings_widget.cpp
|
||||
info/settings/info_settings_widget.h
|
||||
info/similar_channels/info_similar_channels_widget.cpp
|
||||
info/similar_channels/info_similar_channels_widget.h
|
||||
info/statistics/info_statistics_common.h
|
||||
info/statistics/info_statistics_inner_widget.cpp
|
||||
info/statistics/info_statistics_inner_widget.h
|
||||
@@ -1028,6 +1046,8 @@ PRIVATE
|
||||
media/stories/media_stories_recent_views.h
|
||||
media/stories/media_stories_reply.cpp
|
||||
media/stories/media_stories_reply.h
|
||||
media/stories/media_stories_repost_view.cpp
|
||||
media/stories/media_stories_repost_view.h
|
||||
media/stories/media_stories_share.cpp
|
||||
media/stories/media_stories_share.h
|
||||
media/stories/media_stories_sibling.cpp
|
||||
@@ -1278,8 +1298,8 @@ PRIVATE
|
||||
settings/settings_calls.h
|
||||
settings/settings_codes.cpp
|
||||
settings/settings_codes.h
|
||||
settings/settings_common.cpp
|
||||
settings/settings_common.h
|
||||
settings/settings_common_session.cpp
|
||||
settings/settings_common_session.h
|
||||
settings/settings_experimental.cpp
|
||||
settings/settings_experimental.h
|
||||
settings/settings_folders.cpp
|
||||
@@ -1439,6 +1459,7 @@ PRIVATE
|
||||
window/window_section_common.h
|
||||
window/window_session_controller.cpp
|
||||
window/window_session_controller.h
|
||||
window/window_session_controller_link_info.h
|
||||
window/window_slide_animation.cpp
|
||||
window/window_slide_animation.h
|
||||
window/window_top_bar_wrap.h
|
||||
@@ -1729,6 +1750,10 @@ endif()
|
||||
set_target_properties(Telegram PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder})
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
delayimp
|
||||
)
|
||||
target_link_options(Telegram
|
||||
PRIVATE
|
||||
/DELAYLOAD:secur32.dll
|
||||
@@ -1785,6 +1810,10 @@ if (NOT DESKTOP_APP_DISABLE_AUTOUPDATE AND NOT build_macstore AND NOT build_wins
|
||||
)
|
||||
target_include_directories(Updater PRIVATE ${lib_base_loc})
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
target_link_libraries(Updater
|
||||
PRIVATE
|
||||
delayimp
|
||||
)
|
||||
target_link_options(Updater
|
||||
PRIVATE
|
||||
/DELAYLOAD:user32.dll
|
||||
|
||||
BIN
Telegram/Resources/animations/voice_ttl_idle.tgs
Normal file
BIN
Telegram/Resources/animations/voice_ttl_start.tgs
Normal file
BIN
Telegram/Resources/art/winners.tgs
Normal file
|
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 785 B After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/Resources/icons/chat/mini_lock.png
Normal file
|
After Width: | Height: | Size: 266 B |
BIN
Telegram/Resources/icons/chat/mini_lock@2x.png
Normal file
|
After Width: | Height: | Size: 358 B |
BIN
Telegram/Resources/icons/chat/mini_lock@3x.png
Normal file
|
After Width: | Height: | Size: 489 B |
BIN
Telegram/Resources/icons/chat/mini_subscribers.png
Normal file
|
After Width: | Height: | Size: 261 B |
BIN
Telegram/Resources/icons/chat/mini_subscribers@2x.png
Normal file
|
After Width: | Height: | Size: 399 B |
BIN
Telegram/Resources/icons/chat/mini_subscribers@3x.png
Normal file
|
After Width: | Height: | Size: 557 B |
BIN
Telegram/Resources/icons/dialogs/avatar_hidden.png
Normal file
|
After Width: | Height: | Size: 919 B |
BIN
Telegram/Resources/icons/dialogs/avatar_hidden@2x.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/Resources/icons/dialogs/avatar_hidden@3x.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
Telegram/Resources/icons/dialogs/avatar_notes.png
Normal file
|
After Width: | Height: | Size: 753 B |
BIN
Telegram/Resources/icons/dialogs/avatar_notes@2x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/dialogs/avatar_notes@3x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
Telegram/Resources/icons/info/edit/stickers_add.png
Normal file
|
After Width: | Height: | Size: 470 B |
BIN
Telegram/Resources/icons/info/edit/stickers_add@2x.png
Normal file
|
After Width: | Height: | Size: 899 B |
BIN
Telegram/Resources/icons/info/edit/stickers_add@3x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/info/info_media_saved.png
Normal file
|
After Width: | Height: | Size: 380 B |
BIN
Telegram/Resources/icons/info/info_media_saved@2x.png
Normal file
|
After Width: | Height: | Size: 664 B |
BIN
Telegram/Resources/icons/info/info_media_saved@3x.png
Normal file
|
After Width: | Height: | Size: 954 B |
BIN
Telegram/Resources/icons/mediaview/mini_repost.png
Normal file
|
After Width: | Height: | Size: 481 B |
BIN
Telegram/Resources/icons/mediaview/mini_repost@2x.png
Normal file
|
After Width: | Height: | Size: 780 B |
BIN
Telegram/Resources/icons/mediaview/mini_repost@3x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/menu/mode_messages.png
Normal file
|
After Width: | Height: | Size: 745 B |
BIN
Telegram/Resources/icons/menu/mode_messages@2x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/menu/mode_messages@3x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/menu/mode_topics.png
Normal file
|
After Width: | Height: | Size: 913 B |
BIN
Telegram/Resources/icons/menu/mode_topics@2x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
Telegram/Resources/icons/menu/mode_topics@3x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
Telegram/Resources/icons/statistics/mini_stats_like.png
Normal file
|
After Width: | Height: | Size: 497 B |
BIN
Telegram/Resources/icons/statistics/mini_stats_like@2x.png
Normal file
|
After Width: | Height: | Size: 976 B |
BIN
Telegram/Resources/icons/statistics/mini_stats_like@3x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/statistics/mini_stats_share.png
Normal file
|
After Width: | Height: | Size: 465 B |
BIN
Telegram/Resources/icons/statistics/mini_stats_share@2x.png
Normal file
|
After Width: | Height: | Size: 855 B |
BIN
Telegram/Resources/icons/statistics/mini_stats_share@3x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/win_quit.png
Normal file
|
After Width: | Height: | Size: 406 B |
BIN
Telegram/Resources/icons/win_quit@2x.png
Normal file
|
After Width: | Height: | Size: 686 B |
BIN
Telegram/Resources/icons/win_quit@3x.png
Normal file
|
After Width: | Height: | Size: 952 B |
@@ -395,6 +395,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_dlg_new_bot_name" = "Bot name";
|
||||
"lng_no_chats" = "Your chats will be here";
|
||||
"lng_no_chats_filter" = "No chats currently belong to this folder.";
|
||||
"lng_no_saved_sublists" = "You can save messages from other chats here.";
|
||||
"lng_contacts_loading" = "Loading...";
|
||||
"lng_contacts_not_found" = "No contacts found";
|
||||
"lng_topics_not_found" = "No topics found.";
|
||||
@@ -591,6 +592,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_section_background" = "Chat background";
|
||||
"lng_settings_bg_from_gallery" = "Choose from gallery";
|
||||
"lng_settings_bg_from_file" = "Choose from file";
|
||||
"lng_settings_bg_remove" = "Remove wallpaper";
|
||||
"lng_settings_bg_theme_edit" = "Edit theme";
|
||||
"lng_settings_bg_theme_create" = "Create new theme";
|
||||
"lng_settings_bg_cloud_themes" = "Custom themes";
|
||||
@@ -813,6 +815,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_manage_enabled_dictionary" = "Dictionary is enabled";
|
||||
"lng_settings_manage_remove_dictionary" = "Remove Dictionary";
|
||||
|
||||
"lng_settings_gift_premium" = "Premium Gifting";
|
||||
"lng_settings_gift_premium_users_confirm" = "Proceed";
|
||||
"lng_settings_gift_premium_users_error#one" = "You can select maximum {count} user.";
|
||||
"lng_settings_gift_premium_users_error#other" = "You can select maximum {count} users.";
|
||||
|
||||
"lng_backgrounds_header" = "Choose Wallpaper";
|
||||
"lng_theme_sure_keep" = "Keep this theme?";
|
||||
"lng_theme_reverting#one" = "Reverting to the old theme in {count} second.";
|
||||
@@ -833,12 +840,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_background_blur" = "Blurred";
|
||||
"lng_background_sure_delete" = "Are you sure you want to delete this background?";
|
||||
"lng_background_other_info" = "{user} will be able to apply this wallpaper";
|
||||
"lng_background_other_channel" = "All subscribers will see this wallpaper";
|
||||
"lng_background_apply1" = "Apply the wallpaper in this chat.";
|
||||
"lng_background_apply2" = "Enjoy the view.";
|
||||
"lng_background_apply_button" = "Apply For This Chat";
|
||||
"lng_background_dimming" = "Background dimming";
|
||||
"lng_background_sure_reset_default" = "Are you sure you want to reset the wallpaper?";
|
||||
"lng_background_reset_default" = "Reset";
|
||||
"lng_background_apply_me" = "Apply for me";
|
||||
"lng_background_apply_both" = "Apply for me and {user}";
|
||||
"lng_background_apply_channel" = "Apply For Channel";
|
||||
|
||||
"lng_download_path_ask" = "Ask download path for each file";
|
||||
"lng_download_path" = "Download path";
|
||||
@@ -1175,6 +1186,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_profile_bot_privacy" = "Bot Privacy Policy";
|
||||
"lng_profile_common_groups#one" = "{count} group in common";
|
||||
"lng_profile_common_groups#other" = "{count} groups in common";
|
||||
"lng_profile_similar_channels#one" = "{count} similar channel";
|
||||
"lng_profile_similar_channels#other" = "{count} similar channels";
|
||||
"lng_profile_saved_messages#one" = "{count} saved message";
|
||||
"lng_profile_saved_messages#other" = "{count} saved messages";
|
||||
"lng_profile_participants_section" = "Members";
|
||||
"lng_profile_subscribers_section" = "Subscribers";
|
||||
"lng_profile_add_contact" = "Add Contact";
|
||||
@@ -1347,6 +1362,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_manage_peer_reactions_none_about" = "Members of the group can't add any reactions to messages.";
|
||||
"lng_manage_peer_reactions_some_title" = "Only allow these reactions";
|
||||
"lng_manage_peer_reactions_available" = "Available reactions";
|
||||
"lng_manage_peer_reactions_own" = "You can also {link} emoji packs and use them as reactions.";
|
||||
"lng_manage_peer_reactions_own_link" = "create your own";
|
||||
"lng_manage_peer_reactions_level#one" = "Your channel needs to reach level **{count}** to use **{same_count}** custom reaction.";
|
||||
"lng_manage_peer_reactions_level#other" = "Your channel needs to reach level **{count}** to use **{same_count}** custom reactions.";
|
||||
"lng_manage_peer_reactions_boost" = "Boost your channel {link}.";
|
||||
"lng_manage_peer_reactions_boost_link" = "here";
|
||||
"lng_manage_peer_reactions_limit" = "Channels can't have more custom reactions.";
|
||||
|
||||
"lng_manage_peer_antispam" = "Aggressive Anti-Spam";
|
||||
"lng_manage_peer_antispam_about" = "Telegram will filter more spam but may occasionally affect ordinary messages. You can report False Positives in Recent Actions.";
|
||||
@@ -1641,11 +1663,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_suggested_video_button" = "View Video";
|
||||
"lng_action_attach_menu_bot_allowed" = "You allowed this bot to message you when you added it in the attachment menu.";
|
||||
"lng_action_webapp_bot_allowed" = "You allowed this bot to message you in his web-app.";
|
||||
"lng_action_set_wallpaper_me" = "You set a new wallpaper for this chat";
|
||||
"lng_action_set_wallpaper" = "{user} set a new wallpaper for this chat";
|
||||
"lng_action_set_wallpaper_me" = "You set a new wallpaper for this chat.";
|
||||
"lng_action_set_wallpaper" = "{user} set a new wallpaper for this chat.";
|
||||
"lng_action_set_wallpaper_both_me" = "You set a new wallpaper for {user} and you.";
|
||||
"lng_action_set_wallpaper_button" = "View Wallpaper";
|
||||
"lng_action_set_same_wallpaper_me" = "You set the same wallpaper for this chat";
|
||||
"lng_action_set_same_wallpaper" = "{user} set the same wallpaper for this chat";
|
||||
"lng_action_set_wallpaper_remove" = "Remove";
|
||||
"lng_action_set_same_wallpaper_me" = "You set the same wallpaper for this chat.";
|
||||
"lng_action_set_same_wallpaper" = "{user} set the same wallpaper for this chat.";
|
||||
"lng_action_topic_created_inside" = "Topic created";
|
||||
"lng_action_topic_closed_inside" = "Topic closed";
|
||||
"lng_action_topic_reopened_inside" = "Topic reopened";
|
||||
@@ -1667,6 +1691,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_story_mention_me_unavailable" = "The story where you mentioned {user} is no longer available.";
|
||||
"lng_action_story_mention_unavailable" = "The story where {user} mentioned you is no longer available.";
|
||||
"lng_action_giveaway_started" = "{from} just started a giveaway of Telegram Premium subscriptions to its followers.";
|
||||
"lng_action_giveaway_results#one" = "{count} winner of the giveaway was randomly selected by Telegram and received private messages with giftcodes.";
|
||||
"lng_action_giveaway_results#other" = "{count} winners of the giveaway were randomly selected by Telegram and received private messages with giftcodes.";
|
||||
"lng_action_giveaway_results_some" = "Some winners of the giveaway were randomly selected by Telegram and received private messages with giftcodes.";
|
||||
"lng_action_giveaway_results_none" = "No winners of the giveaway could be selected.";
|
||||
|
||||
"lng_similar_channels_title" = "Similar channels";
|
||||
"lng_similar_channels_view_all" = "View all";
|
||||
"lng_similar_channels_more" = "More Channels";
|
||||
"lng_similar_channels_premium_all#one" = "Subscribe to {link} to unlock up to **{count}** similar channel.";
|
||||
"lng_similar_channels_premium_all#other" = "Subscribe to {link} to unlock up to **{count}** similar channels.";
|
||||
"lng_similar_channels_premium_all_link" = "Telegram Premium";
|
||||
"lng_similar_channels_show_more" = "Show more channels";
|
||||
|
||||
"lng_premium_gift_duration_months#one" = "for {count} month";
|
||||
"lng_premium_gift_duration_months#other" = "for {count} months";
|
||||
@@ -1679,6 +1715,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_ttl_video_received" = "{from} sent you a self-destructing video. Please view it on your mobile.";
|
||||
"lng_ttl_video_sent" = "You sent a self-destructing video.";
|
||||
"lng_ttl_video_expired" = "Video has expired";
|
||||
"lng_ttl_voice_sent" = "You sent a self-destructing voice messsage.";
|
||||
"lng_ttl_voice_expired" = "Voice message expired";
|
||||
"lng_ttl_round_sent" = "You sent a self-destructing video message.";
|
||||
"lng_ttl_round_expired" = "Round message expired";
|
||||
|
||||
"lng_profile_add_more_after_create" = "You will be able to add more members after you create the group.";
|
||||
"lng_profile_camera_title" = "Capture yourself";
|
||||
@@ -1808,9 +1848,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_forwarded_hidden" = "The account was hidden by the user.";
|
||||
"lng_forwarded_imported" = "This message was imported from another app. It may not be real.";
|
||||
"lng_signed_author" = "Author: {user}";
|
||||
"lng_sponsored" = "sponsored";
|
||||
"lng_recommended" = "recommended";
|
||||
"lng_sponsored_message_title" = "Sponsored";
|
||||
"lng_recommended_message_title" = "Recommended";
|
||||
"lng_edited" = "edited";
|
||||
"lng_commented" = "commented";
|
||||
"lng_edited_date" = "Edited: {date}";
|
||||
"lng_sent_date" = "Sent: {date}";
|
||||
"lng_views_tooltip#one" = "Views: {count}";
|
||||
@@ -1921,6 +1962,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_premium_summary_subtitle_gift#other" = "{user} has gifted you a {count}-months subscription for Telegram Premium.";
|
||||
"lng_premium_summary_subtitle_gift_me#one" = "You gifted {user} a {count}-month subscription for Telegram Premium.";
|
||||
"lng_premium_summary_subtitle_gift_me#other" = "You gifted {user} a {count}-months subscription for Telegram Premium.";
|
||||
"lng_premium_summary_subtitle_wallpapers" = "Wallpapers for Both Sides";
|
||||
"lng_premium_summary_about_wallpapers" = "Set custom wallpapers for you and your chat partner.";
|
||||
"lng_premium_summary_subtitle_stories" = "Upgraded Stories";
|
||||
"lng_premium_summary_about_stories" = "Priority order, stealth mode, permanent views history and more.";
|
||||
"lng_premium_summary_subtitle_double_limits" = "Doubled Limits";
|
||||
@@ -2028,6 +2071,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_premium_gift_per" = "{cost} / month";
|
||||
"lng_premium_gift_terms" = "You can review the list of features and terms of use for Telegram Premium {link}.";
|
||||
"lng_premium_gift_terms_link" = "here";
|
||||
"lng_premium_gifts_about_user1" = "Give **{user}** access to exclusive features.";
|
||||
"lng_premium_gifts_about_user2" = "Give **{user}** and **{second_user}** access to exclusive features.";
|
||||
"lng_premium_gifts_about_user3" = "Give **{user}**, **{second_user}** and **{name}** access to exclusive features.";
|
||||
"lng_premium_gifts_about_user_more#one" = "Give **{user}**, **{second_user}**, **{name}** and **{count}** more friend access to exclusive features.";
|
||||
"lng_premium_gifts_about_user_more#other" = "Give **{user}**, **{second_user}**, **{name}** and **{count}** more friends access to exclusive features.";
|
||||
"lng_premium_gifts_about_reward#one" = "You will receive {emoji}**{count}** boost.";
|
||||
"lng_premium_gifts_about_reward#other" = "You will receive {emoji}**{count}** boosts.";
|
||||
"lng_premium_gifts_about_paid_title" = "Gifts Sent!";
|
||||
"lng_premium_gifts_about_paid1" = "**{user}** has been notified about the gifts you purchased.";
|
||||
"lng_premium_gifts_about_paid2" = "**{user}** and **{second_user}** have been notified about the gifts you purchased.";
|
||||
"lng_premium_gifts_about_paid3" = "**{user}**, **{second_user}** and **{name}** have been notified about the gifts you purchased.";
|
||||
"lng_premium_gifts_about_paid_more#one" = "**{user}**, **{second_user}**, **{name}** and **{count}** other have been notified about the gifts you purchased.";
|
||||
"lng_premium_gifts_about_paid_more#other" = "**{user}**, **{second_user}**, **{name}** and **{count}** others have been notified about the gifts you purchased.";
|
||||
"lng_premium_gifts_about_paid_below#one" = "They now have access to additional features.";
|
||||
"lng_premium_gifts_about_paid_below#other" = "They now have access to additional features.";
|
||||
"lng_premium_gifts_summary_subtitle" = "What's Included";
|
||||
"lng_premium_gifts_terms" = "By gifting Telegram Premium, you agree to the Telegram {link} and {policy}.";
|
||||
"lng_premium_gifts_terms_policy" = "Privacy Policy";
|
||||
|
||||
"lng_boost_channel_button" = "Boost Channel";
|
||||
"lng_boost_again_button" = "Boost Again";
|
||||
@@ -2083,6 +2144,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_boost_channel_title_color" = "Enable colors";
|
||||
"lng_boost_channel_needs_level_color#one" = "Your channel needs to reach **Level {count}** to change channel color.";
|
||||
"lng_boost_channel_needs_level_color#other" = "Your channel needs to reach **Level {count}** to change channel color.";
|
||||
|
||||
"lng_boost_channel_title_wallpaper" = "Enable wallpapers";
|
||||
"lng_boost_channel_needs_level_wallpaper#one" = "Your channel needs to reach **Level {count}** to change channel wallpaper.";
|
||||
"lng_boost_channel_needs_level_wallpaper#other" = "Your channel needs to reach **Level {count}** to change channel wallpaper.";
|
||||
|
||||
"lng_boost_channel_title_status" = "Enable emoji status";
|
||||
"lng_boost_channel_needs_level_status#one" = "Your channel needs to reach **Level {count}** to set emoji status.";
|
||||
"lng_boost_channel_needs_level_status#other" = "Your channel needs to reach **Level {count}** to set emoji status.";
|
||||
|
||||
"lng_boost_channel_title_reactions" = "Custom reactions";
|
||||
"lng_boost_channel_needs_level_reactions#one" = "Your channel needs to reach **Level {count}** to add **{same_count}** custom emoji as a reaction.";
|
||||
"lng_boost_channel_needs_level_reactions#other" = "Your channel needs to reach **Level {count}** to add **{same_count}** custom emoji as reactions.";
|
||||
|
||||
"lng_boost_channel_ask" = "Ask your **Premium** subscribers to boost your channel with this link:";
|
||||
"lng_boost_channel_ask_button" = "Copy Link";
|
||||
"lng_boost_channel_or" = "or";
|
||||
@@ -2138,6 +2212,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_giveaway_maximum_users_error#other" = "You can select maximum {count} users.";
|
||||
"lng_giveaway_channels_confirm_title" = "Channel is Private";
|
||||
"lng_giveaway_channels_confirm_about" = "Are you sure you want to add a private channel? Users won't be able to join it without an invite link.";
|
||||
"lng_giveaway_additional_prizes" = "Additional prizes";
|
||||
"lng_giveaway_additional_about" = "Turn this on if you want to give the winners your own prizes in addition to Premium subscriptions.";
|
||||
"lng_giveaway_additional_prizes_ph" = "Enter your prize";
|
||||
"lng_giveaway_prizes_just_premium#one" = "All prizes: **{count}** Telegram Premium subscription {duration}.";
|
||||
"lng_giveaway_prizes_just_premium#other" = "All prizes: **{count}** Telegram Premium subscriptions {duration}.";
|
||||
"lng_giveaway_prizes_additional#one" = "All prizes: **{count}** {prize} with Telegram Premium subscription {duration}.";
|
||||
"lng_giveaway_prizes_additional#other" = "All prizes: **{count}** {prize} with Telegram Premium subscriptions {duration}.";
|
||||
"lng_giveaway_show_winners" = "Show winners";
|
||||
"lng_giveaway_show_winners_about" = "Choose whether to make the list of winners public when the giveaway ends.";
|
||||
|
||||
"lng_giveaway_created_title" = "Giveaway created";
|
||||
"lng_giveaway_created_body" = "Check your channels' {link} to see how this giveaway boosted your channel.";
|
||||
@@ -2157,6 +2240,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_prizes_title#one" = "Giveaway Prize";
|
||||
"lng_prizes_title#other" = "Giveaway Prizes";
|
||||
"lng_prizes_additional#one" = "**{count}** {prize}";
|
||||
"lng_prizes_additional#other" = "**{count}** {prize}";
|
||||
"lng_prizes_additional_with" = "with";
|
||||
"lng_prizes_about#one" = "**{count}** Telegram Premium Subscription {duration}.";
|
||||
"lng_prizes_about#other" = "**{count}** Telegram Premium Subscriptions {duration}.";
|
||||
"lng_prizes_participants" = "Participants";
|
||||
@@ -2175,6 +2261,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_prizes_end_text" = "This giveaway was sponsored by {admins}.";
|
||||
"lng_prizes_admins#one" = "the admins of {channel}, who aquired **{count} Telegram Premium** subscription {duration} for its followers";
|
||||
"lng_prizes_admins#other" = "the admins of {channel}, who aquired **{count} Telegram Premium** subscriptions {duration} for its followers.";
|
||||
"lng_prizes_additional_added#one" = "{channel} also included **{count} {prize}** in the prize. Admins of the channel are responsible for delivering this prize.";
|
||||
"lng_prizes_additional_added#other" = "{channel} also included **{count} {prize}** in the prizes. Admins of the channel are responsible for delivering these prizes.";
|
||||
"lng_prizes_how_when_finish" = "On {date}, Telegram will automatically select {winners}.";
|
||||
"lng_prizes_end_when_finish" = "On {date}, Telegram automatically selected {winners}.";
|
||||
"lng_prizes_end_activated#one" = "**{count}** of the winners already used their gift link.";
|
||||
@@ -2200,6 +2288,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_prizes_cancelled" = "The channel cancelled the prizes by reversing the payment for them.";
|
||||
"lng_prizes_badge" = "x{amount}";
|
||||
|
||||
"lng_prizes_results_title" = "Winners Selected!";
|
||||
"lng_prizes_results_about#one" = "**{count}** winner of the {link} was randomly selected by Telegram.";
|
||||
"lng_prizes_results_about#other" = "**{count}** winners of the {link} were randomly selected by Telegram.";
|
||||
"lng_prizes_results_link" = "Giveaway";
|
||||
"lng_prizes_results_winners" = "Winners";
|
||||
"lng_prizes_results_more#one" = "and {count} more!";
|
||||
"lng_prizes_results_more#other" = "and {count} more!";
|
||||
"lng_prizes_results_all" = "All winners received gift links in private messages.";
|
||||
"lng_prizes_results_some" = "Some winners couldn't be selected.";
|
||||
|
||||
"lng_gift_link_title" = "Gift Link";
|
||||
"lng_gift_link_about" = "This link allows you to activate\na **Telegram Premium** subscription.";
|
||||
"lng_gift_link_label_from" = "From";
|
||||
@@ -2403,6 +2501,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_saved_short" = "Save";
|
||||
"lng_saved_forward_here" = "Forward messages here for quick access";
|
||||
"lng_saved_quote_here" = "Quote here to save";
|
||||
"lng_saved_open_chat" = "Open Chat";
|
||||
"lng_saved_open_channel" = "Open Channel";
|
||||
"lng_saved_open_group" = "Open Group";
|
||||
"lng_saved_about_hidden" = "Senders of this messages restricted to link their name when forwarding.";
|
||||
|
||||
"lng_scheduled_messages" = "Scheduled Messages";
|
||||
"lng_scheduled_messages_empty" = "No scheduled messages here yet...";
|
||||
@@ -2429,6 +2531,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_comments_open_none" = "Leave a comment";
|
||||
"lng_replies_view_original" = "View in chat";
|
||||
"lng_replies_messages" = "Replies";
|
||||
"lng_hidden_author_messages" = "Author Hidden";
|
||||
"lng_my_notes" = "My Notes";
|
||||
"lng_replies_discussion_started" = "Discussion started";
|
||||
"lng_replies_no_comments" = "No comments here yet...";
|
||||
|
||||
@@ -2746,6 +2850,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_share_wrong_user" = "This game was opened from a different user.";
|
||||
"lng_share_game_link_copied" = "Game link copied to clipboard.";
|
||||
"lng_share_done" = "Done!";
|
||||
"lng_share_message_to_saved_messages" = "Message forwarded to **Saved Messages**.";
|
||||
"lng_share_messages_to_saved_messages" = "Messages forwarded to **Saved Messages**.";
|
||||
"lng_share_message_to_chat" = "Message forwarded to **{chat}**.";
|
||||
"lng_share_messages_to_chat" = "Messages forwarded to **{chat}**.";
|
||||
"lng_share_message_to_two_chats" = "Message forwarded to **{user}** and **{chat}**.";
|
||||
"lng_share_messages_to_two_chats" = "Messages forwarded to **{user}** and **{chat}**.";
|
||||
"lng_share_message_to_many_chats#one" = "Message forwarded to **{count} chat**.";
|
||||
"lng_share_message_to_many_chats#other" = "Message forwarded to **{count} chats**.";
|
||||
"lng_share_messages_to_many_chats#one" = "Messages forwarded to **{count} chat**.";
|
||||
"lng_share_messages_to_many_chats#other" = "Messages forwarded to **{count} chats**.";
|
||||
|
||||
"lng_contact_phone" = "Phone Number";
|
||||
"lng_enter_contact_data" = "New Contact";
|
||||
@@ -2759,6 +2873,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edit_sign_messages" = "Sign messages";
|
||||
"lng_edit_group" = "Edit group";
|
||||
"lng_edit_channel_color" = "Change name color";
|
||||
"lng_edit_channel_level_min" = "Level 1+";
|
||||
"lng_edit_channel_wallpaper" = "Channel wallpaper";
|
||||
"lng_edit_channel_wallpaper_about" = "Set a wallpaper that will be visible for everyone reading your channel.";
|
||||
"lng_edit_channel_status" = "Channel emoji status";
|
||||
"lng_edit_channel_status_about" = "Choose a status that will be shown next to the channel's name.";
|
||||
"lng_edit_self_title" = "Edit your name";
|
||||
"lng_confirm_contact_data" = "New Contact";
|
||||
"lng_add_contact" = "Create";
|
||||
@@ -3294,6 +3413,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_audio_player_reverse" = "Reverse order";
|
||||
"lng_audio_player_shuffle" = "Shuffle";
|
||||
"lng_audio_transcribe_long" = "This voice message is too long.";
|
||||
"lng_audio_transcribe_trials_left#one" = "You have {count} free transcription left until {date}.";
|
||||
"lng_audio_transcribe_trials_left#other" = "You have {count} free transcriptions left until {date}.";
|
||||
"lng_audio_transcribe_trials_over" = "You have used all your free transcriptions this week. Wait until {date} to use it again or subscribe to {link} now.";
|
||||
|
||||
"lng_rights_edit_admin" = "Manage permissions";
|
||||
"lng_rights_edit_admin_header" = "What can this admin do?";
|
||||
@@ -3570,6 +3692,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_set_background_emoji" = "{from} set channel background emoji to {emoji}";
|
||||
"lng_admin_log_change_background_emoji" = "{from} changed channel background emoji from {previous} to {emoji}";
|
||||
"lng_admin_log_removed_background_emoji" = "{from} removed channel background emoji {emoji}";
|
||||
"lng_admin_log_change_profile_color" = "{from} changed channel profile color from {previous} to {color}";
|
||||
"lng_admin_log_set_profile_background_emoji" = "{from} set channel profile background emoji to {emoji}";
|
||||
"lng_admin_log_change_profile_background_emoji" = "{from} changed channel profile background emoji from {previous} to {emoji}";
|
||||
"lng_admin_log_removed_profile_background_emoji" = "{from} removed channel profile background emoji {emoji}";
|
||||
"lng_admin_log_change_wallpaper" = "{from} changed channel wallpaper";
|
||||
"lng_admin_log_set_status" = "{from} set channel emoji status to {emoji}";
|
||||
"lng_admin_log_change_status" = "{from} changed channel emoji status from {previous} to {emoji}";
|
||||
"lng_admin_log_removed_status" = "{from} removed channel emoji status {emoji}";
|
||||
"lng_admin_log_set_status_until" = "{from} set channel emoji status to {emoji} until {date}";
|
||||
"lng_admin_log_change_status_until" = "{from} changed channel emoji status from {previous} to {emoji} until {date}";
|
||||
"lng_admin_log_user_with_username" = "{name} ({mention})";
|
||||
"lng_admin_log_messages_ttl_set" = "{from} enabled messages auto-delete after {duration}";
|
||||
"lng_admin_log_messages_ttl_changed" = "{from} changed messages auto-delete period from {previous} to {duration}";
|
||||
@@ -4096,6 +4228,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_view_button_request_join" = "Request to Join";
|
||||
"lng_view_button_external_link" = "Open link";
|
||||
"lng_view_button_boost" = "Boost";
|
||||
"lng_view_button_giftcode" = "Open";
|
||||
|
||||
"lng_sponsored_hide_ads" = "Hide";
|
||||
"lng_sponsored_title" = "What are sponsored messages?";
|
||||
@@ -4141,10 +4274,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_forum_topics_no_discussion" = "Topics can't be enabled in discussion groups at the moment.";
|
||||
"lng_forum_choose_title_and_icon" = "Choose title and icon for your topic";
|
||||
"lng_forum_replies_only" = "You can reply to messages in topics.";
|
||||
"lng_forum_message_in" = "Message in {topic}";
|
||||
"lng_forum_reply_in" = "Reply in {topic}";
|
||||
"lng_forum_no_topics" = "No topics currently created in this forum.";
|
||||
"lng_forum_create_topic" = "Create topic";
|
||||
"lng_forum_discard_sure" = "Are you sure you want to discard this topic?";
|
||||
"lng_forum_view_as_messages" = "View as Messages";
|
||||
"lng_forum_view_as_topics" = "View as Topics";
|
||||
"lng_forum_no_messages" = "No messages";
|
||||
"lng_forum_messages#one" = "{count} message";
|
||||
"lng_forum_messages#other" = "{count} messages";
|
||||
@@ -4158,6 +4294,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_request_peer_confirm_rights" = "This will also add {bot} to {chat} with the following rights: {rights}.";
|
||||
"lng_request_peer_confirm_send" = "Send";
|
||||
"lng_request_user_title" = "Choose User";
|
||||
"lng_request_users_title" = "Choose Users";
|
||||
"lng_request_user_premium_yes" = "The user should have a Premium subscription.";
|
||||
"lng_request_user_premium_no" = "The user shouldn't have a Premium subscription.";
|
||||
"lng_request_user_no" = "No such users";
|
||||
@@ -4286,12 +4423,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_stats_title" = "Statistics";
|
||||
"lng_stats_message_title" = "Message Statistic";
|
||||
"lng_stats_story_title" = "Story Statistic";
|
||||
"lng_stats_zoom_out" = "Zoom Out";
|
||||
|
||||
"lng_stats_day_month_year" = "{days_count} {month} {year}";
|
||||
"lng_stats_day_month" = "{days_count} {month}";
|
||||
"lng_stats_weekday_day_month_year" = "{day}, {days_count} {month} {year}";
|
||||
"lng_stats_weekday_day_month_time" = "{day}, {days_count} {month} {time}";
|
||||
|
||||
"lng_stats_overview_title" = "Overview";
|
||||
"lng_stats_overview_member_count" = "Followers";
|
||||
"lng_stats_overview_mean_view_count" = "Views Per Post";
|
||||
"lng_stats_overview_mean_share_count" = "Shared Per Post";
|
||||
"lng_stats_overview_mean_reactions_count" = "Reactions Per Post";
|
||||
"lng_stats_overview_mean_story_view_count" = "Views Per Story";
|
||||
"lng_stats_overview_mean_story_share_count" = "Shared Per Story";
|
||||
"lng_stats_overview_mean_story_reactions_count" = "Reactions Per Story";
|
||||
"lng_stats_overview_enabled_notifications" = "Enabled Notifications";
|
||||
"lng_stats_overview_messages" = "Messages";
|
||||
"lng_stats_overview_group_mean_view_count" = "Viewing Members";
|
||||
@@ -4321,8 +4468,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_stats_recent_messages_title" = "Recent posts";
|
||||
"lng_stats_recent_messages_views#one" = "{count} view";
|
||||
"lng_stats_recent_messages_views#other" = "{count} views";
|
||||
"lng_stats_recent_messages_shares#one" = "{count} share";
|
||||
"lng_stats_recent_messages_shares#other" = "{count} shares";
|
||||
|
||||
"lng_stats_loading" = "Loading stats...";
|
||||
"lng_stats_loading_subtext" = "Please wait a few moments while we generate your stats.";
|
||||
@@ -4336,6 +4481,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_chart_title_language" = "Languages";
|
||||
"lng_chart_title_message_interaction" = "Interactions";
|
||||
"lng_chart_title_instant_view_interaction" = "IV Interactions";
|
||||
"lng_chart_title_reactions_by_emotion" = "Reactions";
|
||||
"lng_chart_title_story_interactions" = "Story Interactions";
|
||||
"lng_chart_title_story_reactions_by_emotion" = "Story reactions";
|
||||
|
||||
"lng_chart_title_group_join" = "Group members";
|
||||
"lng_chart_title_group_join_by_source" = "New members by source";
|
||||
|
||||
@@ -11,5 +11,7 @@
|
||||
<file alias="ttl.tgs">../../animations/ttl.tgs</file>
|
||||
<file alias="discussion.tgs">../../animations/discussion.tgs</file>
|
||||
<file alias="stats.tgs">../../animations/stats.tgs</file>
|
||||
<file alias="voice_ttl_idle.tgs">../../animations/voice_ttl_idle.tgs</file>
|
||||
<file alias="voice_ttl_start.tgs">../../animations/voice_ttl_start.tgs</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<file alias="art/slot_2_idle.tgs">../../art/slot_2_idle.tgs</file>
|
||||
<file alias="art/slot_back.tgs">../../art/slot_back.tgs</file>
|
||||
<file alias="art/slot_pull.tgs">../../art/slot_pull.tgs</file>
|
||||
<file alias="art/winners.tgs">../../art/winners.tgs</file>
|
||||
<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>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="4.11.8.0" />
|
||||
Version="4.14.4.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,11,8,0
|
||||
PRODUCTVERSION 4,11,8,0
|
||||
FILEVERSION 4,14,4,0
|
||||
PRODUCTVERSION 4,14,4,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "4.11.8.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
|
||||
VALUE "FileVersion", "4.14.4.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "4.11.8.0"
|
||||
VALUE "ProductVersion", "4.14.4.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,11,8,0
|
||||
PRODUCTVERSION 4,11,8,0
|
||||
FILEVERSION 4,14,4,0
|
||||
PRODUCTVERSION 4,14,4,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "4.11.8.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
|
||||
VALUE "FileVersion", "4.14.4.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "4.11.8.0"
|
||||
VALUE "ProductVersion", "4.14.4.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -6,6 +6,7 @@ For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
@@ -537,11 +537,12 @@ HANDLE _generateDumpFileAtPath(const WCHAR *path) {
|
||||
|
||||
GetLocalTime(&stLocalTime);
|
||||
|
||||
wsprintf(szFileName, L"%s%s-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
|
||||
szPath, szExeName, updaterVersionStr,
|
||||
stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
|
||||
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
|
||||
GetCurrentProcessId(), GetCurrentThreadId());
|
||||
wsprintf(
|
||||
szFileName, L"%s%s-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
|
||||
szPath, szExeName, updaterVersionStr,
|
||||
stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
|
||||
stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
|
||||
GetCurrentProcessId(), GetCurrentThreadId());
|
||||
return CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
|
||||
}
|
||||
|
||||
@@ -562,7 +563,7 @@ void _generateDump(EXCEPTION_POINTERS* pExceptionPointers) {
|
||||
DWORD len = GetModuleFileName(GetModuleHandle(0), szPath, maxFileLen);
|
||||
if (!len) return;
|
||||
|
||||
WCHAR *pathEnd = szPath + len;
|
||||
WCHAR *pathEnd = szPath + len;
|
||||
|
||||
if (!_wcsicmp(pathEnd - wcslen(_exeName), _exeName)) {
|
||||
wsprintf(pathEnd - wcslen(_exeName), L"");
|
||||
|
||||
@@ -415,12 +415,16 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
|
||||
const auto peer = item->history()->peer;
|
||||
const auto itemId = item->id;
|
||||
const auto id = int32(button->buttonId);
|
||||
const auto chosen = [=](not_null<PeerData*> result) {
|
||||
const auto chosen = [=](std::vector<not_null<PeerData*>> result) {
|
||||
peer->session().api().request(MTPmessages_SendBotRequestedPeer(
|
||||
peer->input,
|
||||
MTP_int(itemId),
|
||||
MTP_int(id),
|
||||
result->input
|
||||
MTP_vector_from_range(
|
||||
result
|
||||
| ranges::views::transform([](
|
||||
not_null<PeerData*> peer) {
|
||||
return MTPInputPeer(peer->input); }))
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
peer->session().api().applyUpdates(result);
|
||||
}).send();
|
||||
|
||||
@@ -20,12 +20,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/controls/filter_link_header.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/filter_icons.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_filter_icons.h"
|
||||
#include "styles/style_layers.h"
|
||||
@@ -341,7 +341,7 @@ void ToggleChatsController::setupAboveWidget() {
|
||||
_addedTopWidget = container->add(object_ptr<Ui::RpWidget>(container));
|
||||
const auto realAbove = container->add(
|
||||
object_ptr<Ui::VerticalLayout>(container));
|
||||
AddDivider(realAbove);
|
||||
Ui::AddDivider(realAbove);
|
||||
const auto totalCount = [&] {
|
||||
if (_chats.empty()) {
|
||||
return _additional.size();
|
||||
@@ -422,7 +422,7 @@ void ToggleChatsController::setupBelowWidget() {
|
||||
auto widget = object_ptr<Ui::DividerLabel>(
|
||||
(QWidget*)nullptr,
|
||||
std::move(layout),
|
||||
st::settingsDividerLabelPadding);
|
||||
st::defaultBoxDividerLabelPadding);
|
||||
raw->add(object_ptr<Ui::FlatLabel>(
|
||||
raw,
|
||||
(_action == ToggleAction::Removing
|
||||
|
||||
@@ -211,6 +211,29 @@ void ApplyBotsList(
|
||||
Data::PeerUpdate::Flag::FullInfo);
|
||||
}
|
||||
|
||||
[[nodiscard]] ChatParticipants::Channels ParseSimilar(
|
||||
not_null<ChannelData*> channel,
|
||||
const MTPmessages_Chats &chats) {
|
||||
auto result = ChatParticipants::Channels();
|
||||
std::vector<not_null<ChannelData*>>();
|
||||
chats.match([&](const auto &data) {
|
||||
const auto &list = data.vchats().v;
|
||||
result.list.reserve(list.size());
|
||||
for (const auto &chat : list) {
|
||||
const auto peer = channel->owner().processChat(chat);
|
||||
if (const auto channel = peer->asChannel()) {
|
||||
result.list.push_back(channel);
|
||||
}
|
||||
}
|
||||
if constexpr (MTPDmessages_chatsSlice::Is<decltype(data)>()) {
|
||||
if (channel->session().premiumPossible()) {
|
||||
result.more = data.vcount().v - data.vchats().v.size();
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ChatParticipant::ChatParticipant(
|
||||
@@ -685,4 +708,50 @@ void ChatParticipants::unblock(
|
||||
_kickRequests.emplace(kick, requestId);
|
||||
}
|
||||
|
||||
void ChatParticipants::loadSimilarChannels(not_null<ChannelData*> channel) {
|
||||
if (!channel->isBroadcast()) {
|
||||
return;
|
||||
} else if (const auto i = _similar.find(channel); i != end(_similar)) {
|
||||
if (i->second.requestId
|
||||
|| !i->second.channels.more
|
||||
|| !channel->session().premium()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_similar[channel].requestId = _api.request(
|
||||
MTPchannels_GetChannelRecommendations(channel->inputChannel)
|
||||
).done([=](const MTPmessages_Chats &result) {
|
||||
auto &similar = _similar[channel];
|
||||
similar.requestId = 0;
|
||||
auto parsed = ParseSimilar(channel, result);
|
||||
if (similar.channels == parsed) {
|
||||
return;
|
||||
}
|
||||
similar.channels = std::move(parsed);
|
||||
if (const auto history = channel->owner().historyLoaded(channel)) {
|
||||
if (const auto item = history->joinedMessageInstance()) {
|
||||
history->owner().requestItemResize(item);
|
||||
}
|
||||
}
|
||||
_similarLoaded.fire_copy(channel);
|
||||
}).send();
|
||||
}
|
||||
|
||||
auto ChatParticipants::similar(not_null<ChannelData*> channel)
|
||||
-> const Channels & {
|
||||
const auto i = channel->isBroadcast()
|
||||
? _similar.find(channel)
|
||||
: end(_similar);
|
||||
if (i != end(_similar)) {
|
||||
return i->second.channels;
|
||||
}
|
||||
static const auto empty = Channels();
|
||||
return empty;
|
||||
}
|
||||
|
||||
auto ChatParticipants::similarLoaded() const
|
||||
-> rpl::producer<not_null<ChannelData*>> {
|
||||
return _similarLoaded.events();
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -120,7 +120,26 @@ public:
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<PeerData*> participant);
|
||||
|
||||
void loadSimilarChannels(not_null<ChannelData*> channel);
|
||||
|
||||
struct Channels {
|
||||
std::vector<not_null<ChannelData*>> list;
|
||||
int more = 0;
|
||||
|
||||
friend inline bool operator==(
|
||||
const Channels &,
|
||||
const Channels &) = default;
|
||||
};
|
||||
[[nodiscard]] const Channels &similar(not_null<ChannelData*> channel);
|
||||
[[nodiscard]] auto similarLoaded() const
|
||||
-> rpl::producer<not_null<ChannelData*>>;
|
||||
|
||||
private:
|
||||
struct SimilarChannels {
|
||||
Channels channels;
|
||||
mtpRequestId requestId = 0;
|
||||
};
|
||||
|
||||
MTP::Sender _api;
|
||||
|
||||
using PeerRequests = base::flat_map<PeerData*, mtpRequestId>;
|
||||
@@ -143,6 +162,9 @@ private:
|
||||
not_null<PeerData*>>;
|
||||
base::flat_map<KickRequest, mtpRequestId> _kickRequests;
|
||||
|
||||
base::flat_map<not_null<ChannelData*>, SimilarChannels> _similar;
|
||||
rpl::event_stream<not_null<ChannelData*>> _similarLoaded;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -25,6 +25,7 @@ struct SendOptions {
|
||||
bool silent = false;
|
||||
bool handleSupportSwitch = false;
|
||||
bool hideViaBot = false;
|
||||
crl::time ttlSeconds = 0;
|
||||
};
|
||||
[[nodiscard]] SendOptions DefaultSendWhenOnlineOptions();
|
||||
|
||||
|
||||
@@ -79,16 +79,19 @@ MTPInputMedia PrepareUploadedPhoto(
|
||||
not_null<HistoryItem*> item,
|
||||
RemoteFileInfo info) {
|
||||
using Flag = MTPDinputMediaUploadedPhoto::Flag;
|
||||
const auto spoiler = item->media()
|
||||
&& item->media()->hasSpoiler();
|
||||
const auto spoiler = item->media() && item->media()->hasSpoiler();
|
||||
const auto ttlSeconds = item->media()
|
||||
? item->media()->ttlSeconds()
|
||||
: 0;
|
||||
const auto flags = (spoiler ? Flag::f_spoiler : Flag())
|
||||
| (info.attachedStickers.empty() ? Flag() : Flag::f_stickers);
|
||||
| (info.attachedStickers.empty() ? Flag() : Flag::f_stickers)
|
||||
| (ttlSeconds ? Flag::f_ttl_seconds : Flag());
|
||||
return MTP_inputMediaUploadedPhoto(
|
||||
MTP_flags(flags),
|
||||
info.file,
|
||||
MTP_vector<MTPInputDocument>(
|
||||
ranges::to<QVector<MTPInputDocument>>(info.attachedStickers)),
|
||||
MTP_int(0));
|
||||
MTP_int(ttlSeconds));
|
||||
}
|
||||
|
||||
MTPInputMedia PrepareUploadedDocument(
|
||||
@@ -98,12 +101,15 @@ MTPInputMedia PrepareUploadedDocument(
|
||||
return MTP_inputMediaEmpty();
|
||||
}
|
||||
using Flag = MTPDinputMediaUploadedDocument::Flag;
|
||||
const auto spoiler = item->media()
|
||||
&& item->media()->hasSpoiler();
|
||||
const auto spoiler = item->media() && item->media()->hasSpoiler();
|
||||
const auto ttlSeconds = item->media()
|
||||
? item->media()->ttlSeconds()
|
||||
: 0;
|
||||
const auto flags = (spoiler ? Flag::f_spoiler : Flag())
|
||||
| (info.thumb ? Flag::f_thumb : Flag())
|
||||
| (item->groupId() ? Flag::f_nosound_video : Flag())
|
||||
| (info.attachedStickers.empty() ? Flag::f_stickers : Flag());
|
||||
| (info.attachedStickers.empty() ? Flag::f_stickers : Flag())
|
||||
| (ttlSeconds ? Flag::f_ttl_seconds : Flag());
|
||||
const auto document = item->media()->document();
|
||||
return MTP_inputMediaUploadedDocument(
|
||||
MTP_flags(flags),
|
||||
@@ -113,7 +119,7 @@ MTPInputMedia PrepareUploadedDocument(
|
||||
ComposeSendingDocumentAttributes(document),
|
||||
MTP_vector<MTPInputDocument>(
|
||||
ranges::to<QVector<MTPInputDocument>>(info.attachedStickers)),
|
||||
MTP_int(0));
|
||||
MTP_int(ttlSeconds));
|
||||
}
|
||||
|
||||
bool HasAttachedStickers(MTPInputMedia media) {
|
||||
|
||||
@@ -90,6 +90,7 @@ void MessagesSearch::searchRequest() {
|
||||
(_from
|
||||
? _from->input
|
||||
: MTP_inputPeerEmpty()),
|
||||
MTPInputPeer(), // saved_peer_id
|
||||
MTPint(), // top_msg_id
|
||||
MTP_inputMessagesFilterEmpty(),
|
||||
MTP_int(0), // min_date
|
||||
|
||||
141
Telegram/SourceFiles/api/api_peer_colors.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_peer_colors.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
constexpr auto kRequestEach = 3600 * crl::time(1000);
|
||||
|
||||
} // namespace
|
||||
|
||||
PeerColors::PeerColors(not_null<ApiWrap*> api)
|
||||
: _api(&api->instance())
|
||||
, _timer([=] { request(); }) {
|
||||
request();
|
||||
_timer.callEach(kRequestEach);
|
||||
}
|
||||
|
||||
PeerColors::~PeerColors() = default;
|
||||
|
||||
void PeerColors::request() {
|
||||
if (_requestId) {
|
||||
return;
|
||||
}
|
||||
_requestId = _api.request(MTPhelp_GetPeerColors(
|
||||
MTP_int(_hash)
|
||||
)).done([=](const MTPhelp_PeerColors &result) {
|
||||
_requestId = 0;
|
||||
result.match([&](const MTPDhelp_peerColors &data) {
|
||||
_hash = data.vhash().v;
|
||||
apply(data);
|
||||
}, [](const MTPDhelp_peerColorsNotModified &) {
|
||||
});
|
||||
}).fail([=] {
|
||||
_requestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
std::vector<uint8> PeerColors::suggested() const {
|
||||
return _suggested.current();
|
||||
}
|
||||
|
||||
rpl::producer<std::vector<uint8>> PeerColors::suggestedValue() const {
|
||||
return _suggested.value();
|
||||
}
|
||||
|
||||
auto PeerColors::indicesValue() const
|
||||
-> rpl::producer<Ui::ColorIndicesCompressed> {
|
||||
return rpl::single(_colorIndicesCurrent
|
||||
? *_colorIndicesCurrent
|
||||
: Ui::ColorIndicesCompressed()
|
||||
) | rpl::then(_colorIndicesChanged.events() | rpl::map([=] {
|
||||
return *_colorIndicesCurrent;
|
||||
}));
|
||||
}
|
||||
|
||||
int PeerColors::requiredLevelFor(PeerId channel, uint8 index) const {
|
||||
if (Data::DecideColorIndex(channel) == index) {
|
||||
return 0;
|
||||
} else if (const auto i = _requiredLevels.find(index)
|
||||
; i != end(_requiredLevels)) {
|
||||
return i->second;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PeerColors::apply(const MTPDhelp_peerColors &data) {
|
||||
auto suggested = std::vector<uint8>();
|
||||
auto colors = std::make_shared<
|
||||
std::array<Ui::ColorIndexData, Ui::kColorIndexCount>>();
|
||||
|
||||
using ParsedColor = std::array<uint32, Ui::kColorPatternsCount>;
|
||||
const auto parseColors = [](const MTPhelp_PeerColorSet &set) {
|
||||
return set.match([&](const MTPDhelp_peerColorSet &data) {
|
||||
auto result = ParsedColor();
|
||||
const auto &list = data.vcolors().v;
|
||||
if (list.empty() || list.size() > Ui::kColorPatternsCount) {
|
||||
LOG(("API Error: Bad count for PeerColorSet.colors: %1"
|
||||
).arg(list.size()));
|
||||
return ParsedColor();
|
||||
}
|
||||
auto fill = result.data();
|
||||
for (const auto &color : list) {
|
||||
*fill++ = (uint32(1) << 24) | uint32(color.v);
|
||||
}
|
||||
return result;
|
||||
}, [](const MTPDhelp_peerColorProfileSet &) {
|
||||
LOG(("API Error: peerColorProfileSet in colors result!"));
|
||||
return ParsedColor();
|
||||
});
|
||||
};
|
||||
|
||||
const auto &list = data.vcolors().v;
|
||||
_requiredLevels.clear();
|
||||
suggested.reserve(list.size());
|
||||
for (const auto &color : list) {
|
||||
const auto &data = color.data();
|
||||
const auto colorIndexBare = data.vcolor_id().v;
|
||||
if (colorIndexBare < 0 || colorIndexBare >= Ui::kColorIndexCount) {
|
||||
LOG(("API Error: Bad color index: %1").arg(colorIndexBare));
|
||||
continue;
|
||||
}
|
||||
const auto colorIndex = uint8(colorIndexBare);
|
||||
if (const auto min = data.vchannel_min_level()) {
|
||||
_requiredLevels[colorIndex] = min->v;
|
||||
}
|
||||
if (!data.is_hidden()) {
|
||||
suggested.push_back(colorIndex);
|
||||
}
|
||||
if (const auto light = data.vcolors()) {
|
||||
auto &fields = (*colors)[colorIndex];
|
||||
fields.light = parseColors(*light);
|
||||
if (const auto dark = data.vdark_colors()) {
|
||||
fields.dark = parseColors(*dark);
|
||||
} else {
|
||||
fields.dark = fields.light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_colorIndicesCurrent) {
|
||||
_colorIndicesCurrent = std::make_unique<Ui::ColorIndicesCompressed>(
|
||||
Ui::ColorIndicesCompressed{ std::move(colors) });
|
||||
_colorIndicesChanged.fire({});
|
||||
} else if (*_colorIndicesCurrent->colors != *colors) {
|
||||
_colorIndicesCurrent->colors = std::move(colors);
|
||||
_colorIndicesChanged.fire({});
|
||||
}
|
||||
_suggested = std::move(suggested);
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
51
Telegram/SourceFiles/api/api_peer_colors.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
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"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
class ApiWrap;
|
||||
|
||||
namespace Ui {
|
||||
struct ColorIndicesCompressed;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Api {
|
||||
|
||||
class PeerColors final {
|
||||
public:
|
||||
explicit PeerColors(not_null<ApiWrap*> api);
|
||||
~PeerColors();
|
||||
|
||||
[[nodiscard]] std::vector<uint8> suggested() const;
|
||||
[[nodiscard]] rpl::producer<std::vector<uint8>> suggestedValue() const;
|
||||
[[nodiscard]] auto indicesValue() const
|
||||
-> rpl::producer<Ui::ColorIndicesCompressed>;
|
||||
|
||||
[[nodiscard]] int requiredLevelFor(
|
||||
PeerId channel,
|
||||
uint8 index) const;
|
||||
|
||||
private:
|
||||
void request();
|
||||
void apply(const MTPDhelp_peerColors &data);
|
||||
|
||||
MTP::Sender _api;
|
||||
int32 _hash = 0;
|
||||
|
||||
mtpRequestId _requestId = 0;
|
||||
base::Timer _timer;
|
||||
rpl::variable<std::vector<uint8>> _suggested;
|
||||
base::flat_map<uint8, int> _requiredLevels;
|
||||
rpl::event_stream<> _colorIndicesChanged;
|
||||
std::unique_ptr<Ui::ColorIndicesCompressed> _colorIndicesCurrent;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Api
|
||||
@@ -515,6 +515,7 @@ auto PeerPhoto::emojiList(EmojiListType type) -> EmojiListData & {
|
||||
case EmojiListType::Profile: return _profileEmojiList;
|
||||
case EmojiListType::Group: return _groupEmojiList;
|
||||
case EmojiListType::Background: return _backgroundEmojiList;
|
||||
case EmojiListType::NoChannelStatus: return _noChannelStatusEmojiList;
|
||||
}
|
||||
Unexpected("Type in PeerPhoto::emojiList.");
|
||||
}
|
||||
@@ -551,6 +552,8 @@ void PeerPhoto::requestEmojiList(EmojiListType type) {
|
||||
? send(MTPaccount_GetDefaultProfilePhotoEmojis())
|
||||
: (type == EmojiListType::Group)
|
||||
? send(MTPaccount_GetDefaultGroupPhotoEmojis())
|
||||
: (type == EmojiListType::NoChannelStatus)
|
||||
? send(MTPaccount_GetChannelRestrictedStatusEmojis())
|
||||
: send(MTPaccount_GetDefaultBackgroundEmojis());
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
Profile,
|
||||
Group,
|
||||
Background,
|
||||
NoChannelStatus,
|
||||
};
|
||||
|
||||
struct UserPhoto {
|
||||
@@ -112,6 +113,7 @@ private:
|
||||
EmojiListData _profileEmojiList;
|
||||
EmojiListData _groupEmojiList;
|
||||
EmojiListData _backgroundEmojiList;
|
||||
EmojiListData _noChannelStatusEmojiList;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace {
|
||||
|
||||
[[nodiscard]] GiftCode Parse(const MTPDpayments_checkedGiftCode &data) {
|
||||
return {
|
||||
.from = peerFromMTP(data.vfrom_id()),
|
||||
.from = data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(),
|
||||
.to = data.vto_id() ? peerFromUser(*data.vto_id()) : PeerId(),
|
||||
.giveawayId = data.vgiveaway_msg_id().value_or_empty(),
|
||||
.date = data.vdate().v,
|
||||
@@ -342,15 +342,12 @@ PremiumGiftCodeOptions::PremiumGiftCodeOptions(not_null<PeerData*> peer)
|
||||
rpl::producer<rpl::no_value, QString> PremiumGiftCodeOptions::request() {
|
||||
return [=](auto consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
const auto channel = _peer->asChannel();
|
||||
if (!channel) {
|
||||
return lifetime;
|
||||
}
|
||||
|
||||
using TLOption = MTPPremiumGiftCodeOption;
|
||||
_api.request(MTPpayments_GetPremiumGiftCodeOptions(
|
||||
MTP_flags(
|
||||
MTPpayments_GetPremiumGiftCodeOptions::Flag::f_boost_peer),
|
||||
MTP_flags(_peer->isChannel()
|
||||
? MTPpayments_GetPremiumGiftCodeOptions::Flag::f_boost_peer
|
||||
: MTPpayments_GetPremiumGiftCodeOptions::Flag(0)),
|
||||
_peer->input
|
||||
)).done([=](const MTPVector<TLOption> &result) {
|
||||
auto tlMapOptions = base::flat_map<Amount, QVector<TLOption>>();
|
||||
@@ -420,6 +417,8 @@ const std::vector<int> &PremiumGiftCodeOptions::availablePresets() const {
|
||||
}
|
||||
|
||||
[[nodiscard]] int PremiumGiftCodeOptions::monthsFromPreset(int monthsIndex) {
|
||||
Expects(monthsIndex >= 0 && monthsIndex < _availablePresets.size());
|
||||
|
||||
return _optionsForOnePerson.months[monthsIndex];
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ void InnerFillMessagePostFlags(
|
||||
not_null<PeerData*> peer,
|
||||
MessageFlags &flags) {
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
if (ShouldSendSilent(peer, options)) {
|
||||
flags |= MessageFlag::Silent;
|
||||
}
|
||||
if (!anonymousPost || options.sendAs) {
|
||||
flags |= MessageFlag::HasFromId;
|
||||
return;
|
||||
@@ -399,11 +402,7 @@ void SendConfirmedFile(
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
}
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = ShouldSendSilent(peer, file->to.options);
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (silentPost) {
|
||||
flags |= MessageFlag::Silent;
|
||||
}
|
||||
if (file->to.options.scheduled) {
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
|
||||
@@ -443,11 +442,30 @@ void SendConfirmedFile(
|
||||
MTPDocument(), // alt_document
|
||||
MTPint());
|
||||
} else if (file->type == SendMediaType::Audio) {
|
||||
const auto ttlSeconds = file->to.options.ttlSeconds;
|
||||
const auto isVoice = [&] {
|
||||
return file->document.match([](const MTPDdocumentEmpty &d) {
|
||||
return false;
|
||||
}, [](const MTPDdocument &d) {
|
||||
return ranges::any_of(d.vattributes().v, [&](
|
||||
const MTPDocumentAttribute &attribute) {
|
||||
using Att = MTPDdocumentAttributeAudio;
|
||||
return attribute.match([](const Att &data) -> bool {
|
||||
return data.vflags().v & Att::Flag::f_voice;
|
||||
}, [](const auto &) {
|
||||
return false;
|
||||
});
|
||||
});
|
||||
});
|
||||
}();
|
||||
using Flag = MTPDmessageMediaDocument::Flag;
|
||||
return MTP_messageMediaDocument(
|
||||
MTP_flags(MTPDmessageMediaDocument::Flag::f_document),
|
||||
MTP_flags(Flag::f_document
|
||||
| (isVoice ? Flag::f_voice : Flag())
|
||||
| (ttlSeconds ? Flag::f_ttl_seconds : Flag())),
|
||||
file->document,
|
||||
MTPDocument(), // alt_document
|
||||
MTPint());
|
||||
MTP_int(ttlSeconds));
|
||||
} else {
|
||||
Unexpected("Type in sendFilesConfirmed.");
|
||||
}
|
||||
|
||||
@@ -9,8 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "data/data_story.h"
|
||||
#include "history/history.h"
|
||||
#include "main/main_session.h"
|
||||
#include "statistics/statistics_data_deserialize.h"
|
||||
@@ -62,15 +63,25 @@ constexpr auto kCheckRequestsTimer = 10 * crl::time(1000);
|
||||
tlUnmuted.vpart().v / tlUnmuted.vtotal().v * 100.,
|
||||
0.,
|
||||
100.);
|
||||
using Recent = MTPMessageInteractionCounters;
|
||||
using Recent = MTPPostInteractionCounters;
|
||||
auto recentMessages = ranges::views::all(
|
||||
data.vrecent_message_interactions().v
|
||||
data.vrecent_posts_interactions().v
|
||||
) | ranges::views::transform([&](const Recent &tl) {
|
||||
return Data::StatisticsMessageInteractionInfo{
|
||||
.messageId = tl.data().vmsg_id().v,
|
||||
.viewsCount = tl.data().vviews().v,
|
||||
.forwardsCount = tl.data().vforwards().v,
|
||||
};
|
||||
return tl.match([&](const MTPDpostInteractionCountersStory &data) {
|
||||
return Data::StatisticsMessageInteractionInfo{
|
||||
.storyId = data.vstory_id().v,
|
||||
.viewsCount = data.vviews().v,
|
||||
.forwardsCount = data.vforwards().v,
|
||||
.reactionsCount = data.vreactions().v,
|
||||
};
|
||||
}, [&](const MTPDpostInteractionCountersMessage &data) {
|
||||
return Data::StatisticsMessageInteractionInfo{
|
||||
.messageId = data.vmsg_id().v,
|
||||
.viewsCount = data.vviews().v,
|
||||
.forwardsCount = data.vforwards().v,
|
||||
.reactionsCount = data.vreactions().v,
|
||||
};
|
||||
});
|
||||
}) | ranges::to_vector;
|
||||
|
||||
return {
|
||||
@@ -80,6 +91,15 @@ constexpr auto kCheckRequestsTimer = 10 * crl::time(1000);
|
||||
.memberCount = StatisticalValueFromTL(data.vfollowers()),
|
||||
.meanViewCount = StatisticalValueFromTL(data.vviews_per_post()),
|
||||
.meanShareCount = StatisticalValueFromTL(data.vshares_per_post()),
|
||||
.meanReactionCount = StatisticalValueFromTL(
|
||||
data.vreactions_per_post()),
|
||||
|
||||
.meanStoryViewCount = StatisticalValueFromTL(
|
||||
data.vviews_per_story()),
|
||||
.meanStoryShareCount = StatisticalValueFromTL(
|
||||
data.vshares_per_story()),
|
||||
.meanStoryReactionCount = StatisticalValueFromTL(
|
||||
data.vreactions_per_story()),
|
||||
|
||||
.enabledNotificationsPercentage = unmuted,
|
||||
|
||||
@@ -110,6 +130,15 @@ constexpr auto kCheckRequestsTimer = 10 * crl::time(1000);
|
||||
.instantViewInteractionGraph = StatisticalGraphFromTL(
|
||||
data.viv_interactions_graph()),
|
||||
|
||||
.reactionsByEmotionGraph = StatisticalGraphFromTL(
|
||||
data.vreactions_by_emotion_graph()),
|
||||
|
||||
.storyInteractionsGraph = StatisticalGraphFromTL(
|
||||
data.vstory_interactions_graph()),
|
||||
|
||||
.storyReactionsByEmotionGraph = StatisticalGraphFromTL(
|
||||
data.vstory_reactions_by_emotion_graph()),
|
||||
|
||||
.recentMessageInteractions = std::move(recentMessages),
|
||||
};
|
||||
}
|
||||
@@ -324,7 +353,7 @@ Data::SupergroupStatistics Statistics::supergroupStats() const {
|
||||
|
||||
PublicForwards::PublicForwards(
|
||||
not_null<ChannelData*> channel,
|
||||
FullMsgId fullId)
|
||||
Data::RecentPostId fullId)
|
||||
: StatisticsRequestSender(channel)
|
||||
, _fullId(fullId) {
|
||||
}
|
||||
@@ -335,99 +364,94 @@ void PublicForwards::request(
|
||||
if (_requestId) {
|
||||
return;
|
||||
}
|
||||
const auto offsetPeer = channel()->owner().peer(token.fullId.peer);
|
||||
const auto tlOffsetPeer = offsetPeer
|
||||
? offsetPeer->input
|
||||
: MTP_inputPeerEmpty();
|
||||
constexpr auto kLimit = tl::make_int(100);
|
||||
_requestId = makeRequest(MTPstats_GetMessagePublicForwards(
|
||||
channel()->inputChannel,
|
||||
MTP_int(_fullId.msg),
|
||||
MTP_int(token.rate),
|
||||
tlOffsetPeer,
|
||||
MTP_int(token.fullId.msg),
|
||||
kLimit
|
||||
)).done([=, channel = channel()](const MTPmessages_Messages &result) {
|
||||
using Messages = QVector<FullMsgId>;
|
||||
const auto channel = StatisticsRequestSender::channel();
|
||||
const auto processResult = [=](const MTPstats_PublicForwards &tl) {
|
||||
using Messages = QVector<Data::RecentPostId>;
|
||||
_requestId = 0;
|
||||
|
||||
auto nextToken = Data::PublicForwardsSlice::OffsetToken();
|
||||
const auto process = [&](const MTPVector<MTPMessage> &messages) {
|
||||
auto result = Messages();
|
||||
for (const auto &message : messages.v) {
|
||||
const auto &data = tl.data();
|
||||
auto &owner = channel->owner();
|
||||
|
||||
owner.processUsers(data.vusers());
|
||||
owner.processChats(data.vchats());
|
||||
|
||||
const auto nextToken = data.vnext_offset()
|
||||
? qs(*data.vnext_offset())
|
||||
: Data::PublicForwardsSlice::OffsetToken();
|
||||
|
||||
const auto fullCount = data.vcount().v;
|
||||
|
||||
auto recentList = Messages(data.vforwards().v.size());
|
||||
for (const auto &tlForward : data.vforwards().v) {
|
||||
tlForward.match([&](const MTPDpublicForwardMessage &data) {
|
||||
const auto &message = data.vmessage();
|
||||
const auto msgId = IdFromMessage(message);
|
||||
const auto peerId = PeerFromMessage(message);
|
||||
const auto lastDate = DateFromMessage(message);
|
||||
if (const auto peer = channel->owner().peerLoaded(peerId)) {
|
||||
if (lastDate) {
|
||||
channel->owner().addNewMessage(
|
||||
message,
|
||||
MessageFlags(),
|
||||
NewMessageType::Existing);
|
||||
nextToken.fullId = { peerId, msgId };
|
||||
result.push_back(nextToken.fullId);
|
||||
if (const auto peer = owner.peerLoaded(peerId)) {
|
||||
if (!lastDate) {
|
||||
return;
|
||||
}
|
||||
owner.addNewMessage(
|
||||
message,
|
||||
MessageFlags(),
|
||||
NewMessageType::Existing);
|
||||
recentList.push_back({ .messageId = { peerId, msgId } });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
auto allLoaded = false;
|
||||
auto fullCount = 0;
|
||||
auto messages = result.match([&](const MTPDmessages_messages &data) {
|
||||
channel->owner().processUsers(data.vusers());
|
||||
channel->owner().processChats(data.vchats());
|
||||
auto list = process(data.vmessages());
|
||||
allLoaded = true;
|
||||
fullCount = list.size();
|
||||
return list;
|
||||
}, [&](const MTPDmessages_messagesSlice &data) {
|
||||
channel->owner().processUsers(data.vusers());
|
||||
channel->owner().processChats(data.vchats());
|
||||
auto list = process(data.vmessages());
|
||||
|
||||
if (const auto nextRate = data.vnext_rate()) {
|
||||
const auto rateUpdated = (nextRate->v != token.rate);
|
||||
if (rateUpdated) {
|
||||
nextToken.rate = nextRate->v;
|
||||
} else {
|
||||
allLoaded = true;
|
||||
}, [&](const MTPDpublicForwardStory &data) {
|
||||
const auto story = owner.stories().applySingle(
|
||||
peerFromMTP(data.vpeer()),
|
||||
data.vstory());
|
||||
if (story) {
|
||||
recentList.push_back({ .storyId = story->fullId() });
|
||||
}
|
||||
}
|
||||
fullCount = data.vcount().v;
|
||||
return list;
|
||||
}, [&](const MTPDmessages_channelMessages &data) {
|
||||
channel->owner().processUsers(data.vusers());
|
||||
channel->owner().processChats(data.vchats());
|
||||
auto list = process(data.vmessages());
|
||||
allLoaded = true;
|
||||
fullCount = data.vcount().v;
|
||||
return list;
|
||||
}, [&](const MTPDmessages_messagesNotModified &) {
|
||||
allLoaded = true;
|
||||
return Messages();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const auto allLoaded = nextToken.isEmpty() || (nextToken == token);
|
||||
_lastTotal = std::max(_lastTotal, fullCount);
|
||||
done({
|
||||
.list = std::move(messages),
|
||||
.list = std::move(recentList),
|
||||
.total = _lastTotal,
|
||||
.allLoaded = allLoaded,
|
||||
.token = nextToken,
|
||||
});
|
||||
}).fail([=] {
|
||||
_requestId = 0;
|
||||
}).send();
|
||||
};
|
||||
|
||||
constexpr auto kLimit = tl::make_int(100);
|
||||
if (_fullId.messageId) {
|
||||
_requestId = makeRequest(MTPstats_GetMessagePublicForwards(
|
||||
channel->inputChannel,
|
||||
MTP_int(_fullId.messageId.msg),
|
||||
MTP_string(token),
|
||||
kLimit
|
||||
)).done(processResult).fail([=] { _requestId = 0; }).send();
|
||||
} else if (_fullId.storyId) {
|
||||
_requestId = makeRequest(MTPstats_GetStoryPublicForwards(
|
||||
channel->input,
|
||||
MTP_int(_fullId.storyId.story),
|
||||
MTP_string(token),
|
||||
kLimit
|
||||
)).done(processResult).fail([=] { _requestId = 0; }).send();
|
||||
}
|
||||
}
|
||||
|
||||
MessageStatistics::MessageStatistics(
|
||||
not_null<ChannelData*> channel,
|
||||
FullMsgId fullId)
|
||||
: StatisticsRequestSender(channel)
|
||||
, _publicForwards(channel, fullId)
|
||||
, _publicForwards(channel, { .messageId = fullId })
|
||||
, _fullId(fullId) {
|
||||
}
|
||||
|
||||
MessageStatistics::MessageStatistics(
|
||||
not_null<ChannelData*> channel,
|
||||
FullStoryId storyId)
|
||||
: StatisticsRequestSender(channel)
|
||||
, _publicForwards(channel, { .storyId = storyId })
|
||||
, _storyId(storyId) {
|
||||
}
|
||||
|
||||
Data::PublicForwardsSlice MessageStatistics::firstSlice() const {
|
||||
return _firstSlice;
|
||||
}
|
||||
@@ -438,21 +462,26 @@ void MessageStatistics::request(Fn<void(Data::MessageStatistics)> done) {
|
||||
}
|
||||
const auto requestFirstPublicForwards = [=](
|
||||
const Data::StatisticalGraph &messageGraph,
|
||||
const Data::StatisticalGraph &reactionsGraph,
|
||||
const Data::StatisticsMessageInteractionInfo &info) {
|
||||
_publicForwards.request({}, [=](Data::PublicForwardsSlice slice) {
|
||||
const auto callback = [=](Data::PublicForwardsSlice slice) {
|
||||
const auto total = slice.total;
|
||||
_firstSlice = std::move(slice);
|
||||
done({
|
||||
.messageInteractionGraph = messageGraph,
|
||||
.reactionsByEmotionGraph = reactionsGraph,
|
||||
.publicForwards = total,
|
||||
.privateForwards = info.forwardsCount - total,
|
||||
.views = info.viewsCount,
|
||||
.reactions = info.reactionsCount,
|
||||
});
|
||||
});
|
||||
};
|
||||
_publicForwards.request({}, callback);
|
||||
};
|
||||
|
||||
const auto requestPrivateForwards = [=](
|
||||
const Data::StatisticalGraph &messageGraph) {
|
||||
const Data::StatisticalGraph &messageGraph,
|
||||
const Data::StatisticalGraph &reactionsGraph) {
|
||||
api().request(MTPchannels_GetMessages(
|
||||
channel()->inputChannel,
|
||||
MTP_vector<MTPInputMessage>(
|
||||
@@ -462,6 +491,13 @@ void MessageStatistics::request(Fn<void(Data::MessageStatistics)> done) {
|
||||
const auto process = [&](const MTPVector<MTPMessage> &messages) {
|
||||
const auto &message = messages.v.front();
|
||||
return message.match([&](const MTPDmessage &data) {
|
||||
auto reactionsCount = 0;
|
||||
if (const auto tlReactions = data.vreactions()) {
|
||||
const auto &tlCounts = tlReactions->data().vresults();
|
||||
for (const auto &tlCount : tlCounts.v) {
|
||||
reactionsCount += tlCount.data().vcount().v;
|
||||
}
|
||||
}
|
||||
return Data::StatisticsMessageInteractionInfo{
|
||||
.messageId = IdFromMessage(message),
|
||||
.viewsCount = data.vviews()
|
||||
@@ -470,6 +506,7 @@ void MessageStatistics::request(Fn<void(Data::MessageStatistics)> done) {
|
||||
.forwardsCount = data.vforwards()
|
||||
? data.vforwards()->v
|
||||
: 0,
|
||||
.reactionsCount = reactionsCount,
|
||||
};
|
||||
}, [](const MTPDmessageEmpty &) {
|
||||
return Data::StatisticsMessageInteractionInfo();
|
||||
@@ -488,22 +525,74 @@ void MessageStatistics::request(Fn<void(Data::MessageStatistics)> done) {
|
||||
return Data::StatisticsMessageInteractionInfo();
|
||||
});
|
||||
|
||||
requestFirstPublicForwards(messageGraph, std::move(info));
|
||||
requestFirstPublicForwards(
|
||||
messageGraph,
|
||||
reactionsGraph,
|
||||
std::move(info));
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
requestFirstPublicForwards(messageGraph, {});
|
||||
requestFirstPublicForwards(messageGraph, reactionsGraph, {});
|
||||
}).send();
|
||||
};
|
||||
|
||||
makeRequest(MTPstats_GetMessageStats(
|
||||
MTP_flags(MTPstats_GetMessageStats::Flags(0)),
|
||||
channel()->inputChannel,
|
||||
MTP_int(_fullId.msg.bare)
|
||||
)).done([=](const MTPstats_MessageStats &result) {
|
||||
requestPrivateForwards(
|
||||
StatisticalGraphFromTL(result.data().vviews_graph()));
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
requestPrivateForwards({});
|
||||
}).send();
|
||||
const auto requestStoryPrivateForwards = [=](
|
||||
const Data::StatisticalGraph &messageGraph,
|
||||
const Data::StatisticalGraph &reactionsGraph) {
|
||||
api().request(MTPstories_GetStoriesByID(
|
||||
channel()->input,
|
||||
MTP_vector<MTPint>(1, MTP_int(_storyId.story)))
|
||||
).done([=](const MTPstories_Stories &result) {
|
||||
const auto &storyItem = result.data().vstories().v.front();
|
||||
auto info = storyItem.match([&](const MTPDstoryItem &data) {
|
||||
if (!data.vviews()) {
|
||||
return Data::StatisticsMessageInteractionInfo();
|
||||
}
|
||||
const auto &tlViews = data.vviews()->data();
|
||||
return Data::StatisticsMessageInteractionInfo{
|
||||
.storyId = data.vid().v,
|
||||
.viewsCount = tlViews.vviews_count().v,
|
||||
.forwardsCount = tlViews.vforwards_count().value_or(0),
|
||||
.reactionsCount = tlViews.vreactions_count().value_or(0),
|
||||
};
|
||||
}, [](const auto &) {
|
||||
return Data::StatisticsMessageInteractionInfo();
|
||||
});
|
||||
|
||||
requestFirstPublicForwards(
|
||||
messageGraph,
|
||||
reactionsGraph,
|
||||
std::move(info));
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
requestFirstPublicForwards(messageGraph, reactionsGraph, {});
|
||||
}).send();
|
||||
};
|
||||
|
||||
if (_storyId) {
|
||||
makeRequest(MTPstats_GetStoryStats(
|
||||
MTP_flags(MTPstats_GetStoryStats::Flags(0)),
|
||||
channel()->input,
|
||||
MTP_int(_storyId.story)
|
||||
)).done([=](const MTPstats_StoryStats &result) {
|
||||
const auto &data = result.data();
|
||||
requestStoryPrivateForwards(
|
||||
StatisticalGraphFromTL(data.vviews_graph()),
|
||||
StatisticalGraphFromTL(data.vreactions_by_emotion_graph()));
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
requestStoryPrivateForwards({}, {});
|
||||
}).send();
|
||||
} else {
|
||||
makeRequest(MTPstats_GetMessageStats(
|
||||
MTP_flags(MTPstats_GetMessageStats::Flags(0)),
|
||||
channel()->inputChannel,
|
||||
MTP_int(_fullId.msg.bare)
|
||||
)).done([=](const MTPstats_MessageStats &result) {
|
||||
const auto &data = result.data();
|
||||
requestPrivateForwards(
|
||||
StatisticalGraphFromTL(data.vviews_graph()),
|
||||
StatisticalGraphFromTL(data.vreactions_by_emotion_graph()));
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
requestPrivateForwards({}, {});
|
||||
}).send();
|
||||
}
|
||||
}
|
||||
|
||||
Boosts::Boosts(not_null<PeerData*> peer)
|
||||
@@ -523,6 +612,7 @@ rpl::producer<rpl::no_value, QString> Boosts::request() {
|
||||
_peer->input
|
||||
)).done([=](const MTPpremium_BoostsStatus &result) {
|
||||
const auto &data = result.data();
|
||||
channel->updateLevelHint(data.vlevel().v);
|
||||
const auto hasPremium = !!data.vpremium_audience();
|
||||
const auto premiumMemberCount = hasPremium
|
||||
? std::max(0, int(data.vpremium_audience()->data().vpart().v))
|
||||
|
||||
@@ -68,14 +68,16 @@ private:
|
||||
|
||||
class PublicForwards final : public StatisticsRequestSender {
|
||||
public:
|
||||
PublicForwards(not_null<ChannelData*> channel, FullMsgId fullId);
|
||||
PublicForwards(
|
||||
not_null<ChannelData*> channel,
|
||||
Data::RecentPostId fullId);
|
||||
|
||||
void request(
|
||||
const Data::PublicForwardsSlice::OffsetToken &token,
|
||||
Fn<void(Data::PublicForwardsSlice)> done);
|
||||
|
||||
private:
|
||||
const FullMsgId _fullId;
|
||||
const Data::RecentPostId _fullId;
|
||||
mtpRequestId _requestId = 0;
|
||||
int _lastTotal = 0;
|
||||
|
||||
@@ -86,6 +88,9 @@ public:
|
||||
explicit MessageStatistics(
|
||||
not_null<ChannelData*> channel,
|
||||
FullMsgId fullId);
|
||||
explicit MessageStatistics(
|
||||
not_null<ChannelData*> channel,
|
||||
FullStoryId storyId);
|
||||
|
||||
void request(Fn<void(Data::MessageStatistics)> done);
|
||||
|
||||
@@ -94,6 +99,7 @@ public:
|
||||
private:
|
||||
PublicForwards _publicForwards;
|
||||
const FullMsgId _fullId;
|
||||
const FullStoryId _storyId;
|
||||
|
||||
Data::PublicForwardsSlice _firstSlice;
|
||||
|
||||
|
||||
@@ -7,13 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_transcribes.h"
|
||||
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Api {
|
||||
|
||||
@@ -22,6 +25,44 @@ Transcribes::Transcribes(not_null<ApiWrap*> api)
|
||||
, _api(&api->instance()) {
|
||||
}
|
||||
|
||||
bool Transcribes::trialsSupport() {
|
||||
if (!_trialsSupport) {
|
||||
const auto count = _session->account().appConfig().get<int>(
|
||||
u"transcribe_audio_trial_weekly_number"_q,
|
||||
0);
|
||||
const auto until = _session->account().appConfig().get<int>(
|
||||
u"transcribe_audio_trial_cooldown_until"_q,
|
||||
0);
|
||||
_trialsSupport = (count > 0) || (until > 0);
|
||||
}
|
||||
return *_trialsSupport;
|
||||
}
|
||||
|
||||
TimeId Transcribes::trialsRefreshAt() {
|
||||
if (_trialsRefreshAt < 0) {
|
||||
_trialsRefreshAt = _session->account().appConfig().get<int>(
|
||||
u"transcribe_audio_trial_cooldown_until"_q,
|
||||
0);
|
||||
}
|
||||
return _trialsRefreshAt;
|
||||
}
|
||||
|
||||
int Transcribes::trialsCount() {
|
||||
if (_trialsCount < 0) {
|
||||
_trialsCount = _session->account().appConfig().get<int>(
|
||||
u"transcribe_audio_trial_weekly_number"_q,
|
||||
-1);
|
||||
return std::max(_trialsCount, 0);
|
||||
}
|
||||
return _trialsCount;
|
||||
}
|
||||
|
||||
crl::time Transcribes::trialsMaxLengthMs() const {
|
||||
return 1000 * _session->account().appConfig().get<int>(
|
||||
u"transcribe_audio_trial_duration_max"_q,
|
||||
300);
|
||||
}
|
||||
|
||||
void Transcribes::toggle(not_null<HistoryItem*> item) {
|
||||
const auto id = item->fullId();
|
||||
auto i = _map.find(id);
|
||||
@@ -86,6 +127,23 @@ void Transcribes::load(not_null<HistoryItem*> item) {
|
||||
MTP_int(item->id)
|
||||
)).done([=](const MTPmessages_TranscribedAudio &result) {
|
||||
const auto &data = result.data();
|
||||
|
||||
{
|
||||
const auto trialsCountChanged = data.vtrial_remains_num()
|
||||
&& (_trialsCount != data.vtrial_remains_num()->v);
|
||||
if (trialsCountChanged) {
|
||||
_trialsCount = data.vtrial_remains_num()->v;
|
||||
}
|
||||
const auto refreshAtChanged = data.vtrial_remains_until_date()
|
||||
&& (_trialsRefreshAt != data.vtrial_remains_until_date()->v);
|
||||
if (refreshAtChanged) {
|
||||
_trialsRefreshAt = data.vtrial_remains_until_date()->v;
|
||||
}
|
||||
if (trialsCountChanged) {
|
||||
ShowTrialTranscribesToast(_trialsCount, _trialsRefreshAt);
|
||||
}
|
||||
}
|
||||
|
||||
auto &entry = _map[id];
|
||||
entry.requestId = 0;
|
||||
entry.pending = data.is_pending();
|
||||
|
||||
@@ -36,12 +36,21 @@ public:
|
||||
|
||||
void apply(const MTPDupdateTranscribedAudio &update);
|
||||
|
||||
[[nodiscard]] bool trialsSupport();
|
||||
[[nodiscard]] TimeId trialsRefreshAt();
|
||||
[[nodiscard]] int trialsCount();
|
||||
[[nodiscard]] crl::time trialsMaxLengthMs() const;
|
||||
|
||||
private:
|
||||
void load(not_null<HistoryItem*> item);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
MTP::Sender _api;
|
||||
|
||||
int _trialsCount = -1;
|
||||
std::optional<bool> _trialsSupport;
|
||||
TimeId _trialsRefreshAt = -1;
|
||||
|
||||
base::flat_map<FullMsgId, Entry> _map;
|
||||
base::flat_map<uint64, FullMsgId> _ids;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mtproto/mtproto_dc_options.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_chat.h"
|
||||
@@ -44,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_cloud_manager.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "core/application.h"
|
||||
#include "storage/storage_account.h"
|
||||
@@ -1111,6 +1113,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||
? peerToMTP(_session->userPeerId())
|
||||
: MTP_peerUser(d.vuser_id())),
|
||||
MTP_peerUser(d.vuser_id()),
|
||||
MTPPeer(), // saved_peer_id
|
||||
d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(),
|
||||
MTP_long(d.vvia_bot_id().value_or_empty()),
|
||||
d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(),
|
||||
@@ -1142,6 +1145,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||
d.vid(),
|
||||
MTP_peerUser(d.vfrom_id()),
|
||||
MTP_peerChat(d.vchat_id()),
|
||||
MTPPeer(), // saved_peer_id
|
||||
d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(),
|
||||
MTP_long(d.vvia_bot_id().value_or_empty()),
|
||||
d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(),
|
||||
@@ -1202,11 +1206,12 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||
item->markMediaAndMentionRead();
|
||||
_session->data().requestItemRepaint(item);
|
||||
|
||||
if (item->out()
|
||||
&& item->history()->peer->isUser()
|
||||
&& !requestingDifference()) {
|
||||
item->history()->peer->asUser()->madeAction(
|
||||
base::unixtime::now());
|
||||
if (item->out()) {
|
||||
const auto user = item->history()->peer->asUser();
|
||||
if (user && !requestingDifference()) {
|
||||
user->madeAction(base::unixtime::now());
|
||||
}
|
||||
ClearMediaAsExpired(item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1991,6 +1996,19 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updatePeerWallpaper: {
|
||||
const auto &d = update.c_updatePeerWallpaper();
|
||||
if (const auto peer = session().data().peerLoaded(peerFromMTP(d.vpeer()))) {
|
||||
if (const auto paper = d.vwallpaper()) {
|
||||
peer->setWallPaper(
|
||||
Data::WallPaper::Create(&session(), *paper),
|
||||
d.is_wallpaper_overridden());
|
||||
} else {
|
||||
peer->setWallPaper({});
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateBotCommands: {
|
||||
const auto &d = update.c_updateBotCommands();
|
||||
if (const auto peer = session().data().peerLoaded(peerFromMTP(d.vpeer()))) {
|
||||
@@ -2191,6 +2209,16 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updatePinnedSavedDialogs: {
|
||||
session().data().savedMessages().apply(
|
||||
update.c_updatePinnedSavedDialogs());
|
||||
} break;
|
||||
|
||||
case mtpc_updateSavedDialogPinned: {
|
||||
session().data().savedMessages().apply(
|
||||
update.c_updateSavedDialogPinned());
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannel: {
|
||||
auto &d = update.c_updateChannel();
|
||||
if (const auto channel = session().data().channelLoaded(d.vchannel_id())) {
|
||||
@@ -2326,6 +2354,14 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelViewForumAsMessages: {
|
||||
const auto &d = update.c_updateChannelViewForumAsMessages();
|
||||
const auto id = ChannelId(d.vchannel_id());
|
||||
if (const auto channel = session().data().channelLoaded(id)) {
|
||||
channel->setViewAsMessagesFlag(mtpIsTrue(d.venabled()));
|
||||
}
|
||||
} break;
|
||||
|
||||
// Pinned message.
|
||||
case mtpc_updatePinnedMessages: {
|
||||
const auto &d = update.c_updatePinnedMessages();
|
||||
|
||||
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_hash.h"
|
||||
#include "api/api_invite_links.h"
|
||||
#include "api/api_media.h"
|
||||
#include "api/api_peer_colors.h"
|
||||
#include "api/api_peer_photo.h"
|
||||
#include "api/api_polls.h"
|
||||
#include "api/api_sending.h"
|
||||
@@ -33,50 +34,36 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_user_names.h"
|
||||
#include "api/api_websites.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_web_page.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_sparse_ids.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_search_controller.h"
|
||||
#include "data/data_scheduled_messages.h"
|
||||
#include "data/data_channel_admins.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_wall_paper.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "dialogs/dialogs_key.h"
|
||||
#include "core/core_cloud_password.h"
|
||||
#include "core/application.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "base/random.h"
|
||||
#include "base/qt/qt_common_adapters.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "main/main_account.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "boxes/stickers_box.h"
|
||||
#include "boxes/sticker_set_box.h"
|
||||
#include "boxes/premium_limits_box.h"
|
||||
#include "window/notifications_manager.h"
|
||||
@@ -87,7 +74,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "ui/item_text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/emoji_config.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "support/support_helper.h"
|
||||
@@ -95,9 +81,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/localimageloader.h"
|
||||
#include "storage/download_manager_mtproto.h"
|
||||
#include "storage/file_upload.h"
|
||||
#include "storage/storage_facade.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "storage/storage_account.h"
|
||||
|
||||
namespace {
|
||||
@@ -147,6 +130,15 @@ void ShowChannelsLimitBox(not_null<PeerData*> peer) {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] FileLoadTo FileLoadTaskOptions(const Api::SendAction &action) {
|
||||
const auto peer = action.history->peer;
|
||||
return FileLoadTo(
|
||||
peer->id,
|
||||
action.options,
|
||||
action.replyTo,
|
||||
action.replaceMediaOf);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||
@@ -180,7 +172,8 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||
, _transcribes(std::make_unique<Api::Transcribes>(this))
|
||||
, _premium(std::make_unique<Api::Premium>(this))
|
||||
, _usernames(std::make_unique<Api::Usernames>(this))
|
||||
, _websites(std::make_unique<Api::Websites>(this)) {
|
||||
, _websites(std::make_unique<Api::Websites>(this))
|
||||
, _peerColors(std::make_unique<Api::PeerColors>(this)) {
|
||||
crl::on_main(session, [=] {
|
||||
// You can't use _session->lifetime() in the constructor,
|
||||
// only queued, because it is not constructed yet.
|
||||
@@ -229,11 +222,11 @@ void ApiWrap::setupSupportMode() {
|
||||
void ApiWrap::requestChangelog(
|
||||
const QString &sinceVersion,
|
||||
Fn<void(const MTPUpdates &result)> callback) {
|
||||
request(MTPhelp_GetAppChangelog(
|
||||
MTP_string(sinceVersion)
|
||||
)).done(
|
||||
callback
|
||||
).send();
|
||||
//request(MTPhelp_GetAppChangelog(
|
||||
// MTP_string(sinceVersion)
|
||||
//)).done(
|
||||
// callback
|
||||
//).send();
|
||||
}
|
||||
|
||||
void ApiWrap::refreshTopPromotion() {
|
||||
@@ -448,6 +441,26 @@ void ApiWrap::savePinnedOrder(not_null<Data::Forum*> forum) {
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::savePinnedOrder(not_null<Data::SavedMessages*> saved) {
|
||||
const auto &order = _session->data().pinnedChatsOrder(saved);
|
||||
const auto input = [](Dialogs::Key key) {
|
||||
if (const auto sublist = key.sublist()) {
|
||||
return MTP_inputDialogPeer(sublist->peer()->input);
|
||||
}
|
||||
Unexpected("Key type in pinnedDialogsOrder().");
|
||||
};
|
||||
auto peers = QVector<MTPInputDialogPeer>();
|
||||
peers.reserve(order.size());
|
||||
ranges::transform(
|
||||
order,
|
||||
ranges::back_inserter(peers),
|
||||
input);
|
||||
request(MTPmessages_ReorderPinnedSavedDialogs(
|
||||
MTP_flags(MTPmessages_ReorderPinnedSavedDialogs::Flag::f_force),
|
||||
MTP_vector(peers)
|
||||
)).send();
|
||||
}
|
||||
|
||||
void ApiWrap::toggleHistoryArchived(
|
||||
not_null<History*> history,
|
||||
bool archived,
|
||||
@@ -1739,6 +1752,10 @@ void ApiWrap::joinChannel(not_null<ChannelData*> channel) {
|
||||
}).send();
|
||||
|
||||
_channelAmInRequests.emplace(channel, requestId);
|
||||
|
||||
using Flag = ChannelDataFlag;
|
||||
chatParticipants().loadSimilarChannels(channel);
|
||||
channel->setFlags(channel->flags() | Flag::SimilarExpanded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2055,13 +2072,13 @@ void ApiWrap::deleteHistory(
|
||||
|
||||
void ApiWrap::applyUpdates(
|
||||
const MTPUpdates &updates,
|
||||
uint64 sentMessageRandomId) {
|
||||
uint64 sentMessageRandomId) const {
|
||||
this->updates().applyUpdates(updates, sentMessageRandomId);
|
||||
}
|
||||
|
||||
int ApiWrap::applyAffectedHistory(
|
||||
PeerData *peer,
|
||||
const MTPmessages_AffectedHistory &result) {
|
||||
const MTPmessages_AffectedHistory &result) const {
|
||||
const auto &data = result.c_messages_affectedHistory();
|
||||
if (const auto channel = peer ? peer->asChannel() : nullptr) {
|
||||
channel->ptsUpdateAndApply(data.vpts().v, data.vpts_count().v);
|
||||
@@ -2083,7 +2100,7 @@ void ApiWrap::applyAffectedMessages(
|
||||
}
|
||||
|
||||
void ApiWrap::applyAffectedMessages(
|
||||
const MTPmessages_AffectedMessages &result) {
|
||||
const MTPmessages_AffectedMessages &result) const {
|
||||
const auto &data = result.c_messages_affectedMessages();
|
||||
updates().updateAndApply(data.vpts().v, data.vpts_count().v);
|
||||
}
|
||||
@@ -2542,8 +2559,8 @@ void ApiWrap::refreshFileReference(
|
||||
request(MTPhelp_GetPremiumPromo());
|
||||
}, [&](Data::FileOriginStory data) {
|
||||
request(MTPstories_GetStoriesByID(
|
||||
_session->data().peer(data.peerId)->input,
|
||||
MTP_vector<MTPint>(1, MTP_int(data.storyId))));
|
||||
_session->data().peer(data.peer)->input,
|
||||
MTP_vector<MTPint>(1, MTP_int(data.story))));
|
||||
}, [&](v::null_t) {
|
||||
fail();
|
||||
});
|
||||
@@ -3429,7 +3446,7 @@ void ApiWrap::sendVoiceMessage(
|
||||
crl::time duration,
|
||||
const SendAction &action) {
|
||||
const auto caption = TextWithTags();
|
||||
const auto to = fileLoadTaskOptions(action);
|
||||
const auto to = FileLoadTaskOptions(action);
|
||||
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
||||
&session(),
|
||||
result,
|
||||
@@ -3447,7 +3464,7 @@ void ApiWrap::editMedia(
|
||||
if (list.files.empty()) return;
|
||||
|
||||
auto &file = list.files.front();
|
||||
const auto to = fileLoadTaskOptions(action);
|
||||
const auto to = FileLoadTaskOptions(action);
|
||||
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
||||
&session(),
|
||||
file.path,
|
||||
@@ -3476,7 +3493,7 @@ void ApiWrap::sendFiles(
|
||||
sendMessage(std::move(message));
|
||||
}
|
||||
|
||||
const auto to = fileLoadTaskOptions(action);
|
||||
const auto to = FileLoadTaskOptions(action);
|
||||
if (album) {
|
||||
album->options = to.options;
|
||||
}
|
||||
@@ -3515,7 +3532,7 @@ void ApiWrap::sendFile(
|
||||
const QByteArray &fileContent,
|
||||
SendMediaType type,
|
||||
const SendAction &action) {
|
||||
const auto to = fileLoadTaskOptions(action);
|
||||
const auto to = FileLoadTaskOptions(action);
|
||||
auto caption = TextWithTags();
|
||||
const auto spoiler = false;
|
||||
const auto information = nullptr;
|
||||
@@ -4174,15 +4191,6 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
||||
});
|
||||
}
|
||||
|
||||
FileLoadTo ApiWrap::fileLoadTaskOptions(const SendAction &action) const {
|
||||
const auto peer = action.history->peer;
|
||||
return FileLoadTo(
|
||||
peer->id,
|
||||
action.options,
|
||||
action.replyTo,
|
||||
action.replaceMediaOf);
|
||||
}
|
||||
|
||||
void ApiWrap::reloadContactSignupSilent() {
|
||||
if (_contactSignupSilentRequestId) {
|
||||
return;
|
||||
@@ -4398,3 +4406,7 @@ Api::Usernames &ApiWrap::usernames() {
|
||||
Api::Websites &ApiWrap::websites() {
|
||||
return *_websites;
|
||||
}
|
||||
|
||||
Api::PeerColors &ApiWrap::peerColors() {
|
||||
return *_peerColors;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "api/api_common.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/flat_map.h"
|
||||
#include "base/flat_set.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "data/stickers/data_stickers_set.h"
|
||||
#include "data/data_messages.h"
|
||||
@@ -36,6 +34,7 @@ class Forum;
|
||||
class ForumTopic;
|
||||
class Thread;
|
||||
class Story;
|
||||
class SavedMessages;
|
||||
} // namespace Data
|
||||
|
||||
namespace InlineBots {
|
||||
@@ -73,6 +72,7 @@ class InviteLinks;
|
||||
class ViewsManager;
|
||||
class ConfirmPhone;
|
||||
class PeerPhoto;
|
||||
class PeerColors;
|
||||
class Polls;
|
||||
class ChatParticipants;
|
||||
class UnreadThings;
|
||||
@@ -141,10 +141,10 @@ public:
|
||||
|
||||
void applyUpdates(
|
||||
const MTPUpdates &updates,
|
||||
uint64 sentMessageRandomId = 0);
|
||||
uint64 sentMessageRandomId = 0) const;
|
||||
int applyAffectedHistory(
|
||||
PeerData *peer, // May be nullptr, like for deletePhoneCallHistory.
|
||||
const MTPmessages_AffectedHistory &result);
|
||||
const MTPmessages_AffectedHistory &result) const;
|
||||
|
||||
void registerModifyRequest(const QString &key, mtpRequestId requestId);
|
||||
void clearModifyRequest(const QString &key);
|
||||
@@ -153,6 +153,7 @@ public:
|
||||
|
||||
void savePinnedOrder(Data::Folder *folder);
|
||||
void savePinnedOrder(not_null<Data::Forum*> forum);
|
||||
void savePinnedOrder(not_null<Data::SavedMessages*> saved);
|
||||
void toggleHistoryArchived(
|
||||
not_null<History*> history,
|
||||
bool archived,
|
||||
@@ -392,6 +393,7 @@ public:
|
||||
[[nodiscard]] Api::Premium &premium();
|
||||
[[nodiscard]] Api::Usernames &usernames();
|
||||
[[nodiscard]] Api::Websites &websites();
|
||||
[[nodiscard]] Api::PeerColors &peerColors();
|
||||
|
||||
void updatePrivacyLastSeens();
|
||||
|
||||
@@ -503,7 +505,7 @@ private:
|
||||
not_null<PeerData*> peer,
|
||||
bool justClear,
|
||||
bool revoke);
|
||||
void applyAffectedMessages(const MTPmessages_AffectedMessages &result);
|
||||
void applyAffectedMessages(const MTPmessages_AffectedMessages &result) const;
|
||||
|
||||
void deleteAllFromParticipantSend(
|
||||
not_null<ChannelData*> channel,
|
||||
@@ -532,7 +534,6 @@ private:
|
||||
Api::SendOptions options,
|
||||
uint64 randomId,
|
||||
Fn<void(bool)> done = nullptr);
|
||||
FileLoadTo fileLoadTaskOptions(const SendAction &action) const;
|
||||
|
||||
void getTopPromotionDelayed(TimeId now, TimeId next);
|
||||
void topPromotionDone(const MTPhelp_PromoData &proxy);
|
||||
@@ -711,6 +712,7 @@ private:
|
||||
const std::unique_ptr<Api::Premium> _premium;
|
||||
const std::unique_ptr<Api::Usernames> _usernames;
|
||||
const std::unique_ptr<Api::Websites> _websites;
|
||||
const std::unique_ptr<Api::PeerColors> _peerColors;
|
||||
|
||||
mtpRequestId _wallPaperRequestId = 0;
|
||||
QString _wallPaperSlug;
|
||||
|
||||
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
|
||||
namespace Ui {
|
||||
class LinkButton;
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/call_delayed.h"
|
||||
#include "base/random.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/premium_limits_box.h"
|
||||
#include "boxes/peers/add_participants_box.h"
|
||||
|
||||
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
#include "base/timer.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session_settings.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_auto_download.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
@@ -35,7 +36,6 @@ not_null<int64*> AddSizeLimitSlider(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
const base::flat_map<Type, int64> &values,
|
||||
int64 defaultValue) {
|
||||
using namespace Settings;
|
||||
using Pair = base::flat_map<Type, int64>::value_type;
|
||||
|
||||
const auto limits = Ui::CreateChild<rpl::event_stream<int64>>(
|
||||
@@ -46,7 +46,7 @@ not_null<int64*> AddSizeLimitSlider(
|
||||
[](Pair pair) { return pair.second; })->second;
|
||||
const auto startLimit = currentLimit ? currentLimit : defaultValue;
|
||||
const auto result = Ui::CreateChild<int64>(container.get(), startLimit);
|
||||
AddButtonWithLabel(
|
||||
Settings::AddButtonWithLabel(
|
||||
container,
|
||||
tr::lng_media_size_limit(),
|
||||
limits->events_starting_with_copy(
|
||||
@@ -109,11 +109,11 @@ void AutoDownloadBox::setupContent() {
|
||||
Type type,
|
||||
rpl::producer<QString> label) {
|
||||
const auto value = settings->bytesLimit(_source, type);
|
||||
AddButton(
|
||||
content->add(object_ptr<Ui::SettingsButton>(
|
||||
content,
|
||||
std::move(label),
|
||||
st::settingsButtonNoIcon
|
||||
)->toggleOn(
|
||||
))->toggleOn(
|
||||
rpl::single(value > 0)
|
||||
)->toggledChanges(
|
||||
) | rpl::start_with_next([=](bool enabled) {
|
||||
|
||||
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
|
||||
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
|
||||
namespace Ui {
|
||||
class Radiobutton;
|
||||
|
||||
@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/chat/attach/attach_extensions.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "mtproto/sender.h"
|
||||
@@ -24,7 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_document_media.h"
|
||||
#include "boxes/background_preview_box.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "window/window_session_controller.h"
|
||||
@@ -129,6 +129,8 @@ private:
|
||||
int row) const;
|
||||
void validatePaperThumbnail(const Paper &paper) const;
|
||||
|
||||
[[nodiscard]] bool forChannel() const;
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
PeerData * const _forPeer = nullptr;
|
||||
|
||||
@@ -165,9 +167,9 @@ void BackgroundBox::prepare() {
|
||||
auto wrap = object_ptr<Ui::VerticalLayout>(this);
|
||||
const auto container = wrap.data();
|
||||
|
||||
Settings::AddSkip(container);
|
||||
Ui::AddSkip(container);
|
||||
|
||||
const auto button = container->add(Settings::CreateButton(
|
||||
const auto button = container->add(object_ptr<Ui::SettingsButton>(
|
||||
container,
|
||||
tr::lng_settings_bg_from_file(),
|
||||
st::infoProfileButton));
|
||||
@@ -176,12 +178,29 @@ void BackgroundBox::prepare() {
|
||||
st::infoIconMediaPhoto,
|
||||
st::infoSharedMediaButtonIconPosition);
|
||||
|
||||
if (forChannel() && _forPeer->wallPaper()) {
|
||||
const auto remove = container->add(object_ptr<Ui::SettingsButton>(
|
||||
container,
|
||||
tr::lng_settings_bg_remove(),
|
||||
st::infoBlockButton));
|
||||
object_ptr<Info::Profile::FloatingIcon>(
|
||||
remove,
|
||||
st::infoIconDeleteRed,
|
||||
st::infoSharedMediaButtonIconPosition);
|
||||
|
||||
remove->setClickedCallback([=] {
|
||||
if (const auto resolved = _inner->resolveResetCustomPaper()) {
|
||||
chosen(*resolved);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
button->setClickedCallback([=] {
|
||||
chooseFromFile();
|
||||
});
|
||||
|
||||
Settings::AddSkip(container);
|
||||
Settings::AddDivider(container);
|
||||
Ui::AddSkip(container);
|
||||
Ui::AddDivider(container);
|
||||
|
||||
_inner = container->add(
|
||||
object_ptr<Inner>(this, &_controller->session(), _forPeer));
|
||||
@@ -290,6 +309,23 @@ void BackgroundBox::chosen(const Data::WallPaper &paper) {
|
||||
closeBox();
|
||||
}
|
||||
return;
|
||||
} else if (forChannel()) {
|
||||
if (_forPeer->wallPaper() && _forPeer->wallPaper()->equals(paper)) {
|
||||
closeBox();
|
||||
return;
|
||||
}
|
||||
const auto &themes = _forPeer->owner().cloudThemes();
|
||||
for (const auto &theme : themes.chatThemes()) {
|
||||
for (const auto &[type, themed] : theme.settings) {
|
||||
if (themed.paper && themed.paper->equals(paper)) {
|
||||
_controller->show(Box<BackgroundPreviewBox>(
|
||||
_controller,
|
||||
Data::WallPaper::FromEmojiId(theme.emoticon),
|
||||
BackgroundPreviewArgs{ _forPeer }));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_controller->show(Box<BackgroundPreviewBox>(
|
||||
_controller,
|
||||
@@ -310,12 +346,16 @@ void BackgroundBox::resetForPeer() {
|
||||
}).send();
|
||||
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
_forPeer->setWallPaper(std::nullopt);
|
||||
_forPeer->setWallPaper({});
|
||||
if (weak) {
|
||||
_controller->finishChatThemeEdit(_forPeer);
|
||||
}
|
||||
}
|
||||
|
||||
bool BackgroundBox::forChannel() const {
|
||||
return _forPeer && _forPeer->isChannel();
|
||||
}
|
||||
|
||||
void BackgroundBox::removePaper(const Data::WallPaper &paper) {
|
||||
const auto session = &_controller->session();
|
||||
const auto remove = [=, weak = Ui::MakeWeak(this)](Fn<void()> &&close) {
|
||||
@@ -345,9 +385,16 @@ BackgroundBox::Inner::Inner(
|
||||
, _session(session)
|
||||
, _forPeer(forPeer)
|
||||
, _api(&_session->mtp())
|
||||
, _check(std::make_unique<Ui::RoundCheckbox>(st::overviewCheck, [=] { update(); })) {
|
||||
, _check(
|
||||
std::make_unique<Ui::RoundCheckbox>(
|
||||
st::overviewCheck,
|
||||
[=] { update(); })) {
|
||||
_check->setChecked(true, anim::type::instant);
|
||||
resize(st::boxWideWidth, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
|
||||
resize(
|
||||
st::boxWideWidth,
|
||||
(2 * (st::backgroundSize.height() + st::backgroundPadding)
|
||||
+ st::backgroundPadding));
|
||||
|
||||
Window::Theme::IsNightModeValue(
|
||||
) | rpl::start_with_next([=] {
|
||||
updatePapers();
|
||||
@@ -364,21 +411,31 @@ BackgroundBox::Inner::Inner(
|
||||
_check->invalidateCache();
|
||||
}, lifetime());
|
||||
|
||||
using Update = Window::Theme::BackgroundUpdate;
|
||||
Window::Theme::Background()->updates(
|
||||
) | rpl::start_with_next([=](const Update &update) {
|
||||
if (update.type == Update::Type::New) {
|
||||
sortPapers();
|
||||
requestPapers();
|
||||
this->update();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
if (forChannel()) {
|
||||
_session->data().cloudThemes().chatThemesUpdated(
|
||||
) | rpl::start_with_next([=] {
|
||||
updatePapers();
|
||||
}, lifetime());
|
||||
} else {
|
||||
using Update = Window::Theme::BackgroundUpdate;
|
||||
Window::Theme::Background()->updates(
|
||||
) | rpl::start_with_next([=](const Update &update) {
|
||||
if (update.type == Update::Type::New) {
|
||||
sortPapers();
|
||||
requestPapers();
|
||||
this->update();
|
||||
}
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::requestPapers() {
|
||||
if (forChannel()) {
|
||||
_session->data().cloudThemes().refreshChatThemes();
|
||||
return;
|
||||
}
|
||||
_api.request(MTPaccount_GetWallPapers(
|
||||
MTP_long(_session->data().wallpapersHash())
|
||||
)).done([=](const MTPaccount_WallPapers &result) {
|
||||
@@ -395,7 +452,7 @@ auto BackgroundBox::Inner::resolveResetCustomPaper() const
|
||||
}
|
||||
const auto nonCustom = Window::Theme::Background()->paper();
|
||||
const auto themeEmoji = _forPeer->themeEmoji();
|
||||
if (themeEmoji.isEmpty()) {
|
||||
if (forChannel() || themeEmoji.isEmpty()) {
|
||||
return nonCustom;
|
||||
}
|
||||
const auto &themes = _forPeer->owner().cloudThemes();
|
||||
@@ -443,6 +500,8 @@ void BackgroundBox::Inner::pushCustomPapers() {
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::sortPapers() {
|
||||
Expects(!forChannel());
|
||||
|
||||
const auto currentCustom = _forPeer ? _forPeer->wallPaper() : nullptr;
|
||||
_currentId = currentCustom
|
||||
? currentCustom->id()
|
||||
@@ -472,23 +531,60 @@ void BackgroundBox::Inner::sortPapers() {
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::updatePapers() {
|
||||
if (_session->data().wallpapers().empty()) {
|
||||
return;
|
||||
if (forChannel()) {
|
||||
if (_session->data().cloudThemes().chatThemes().empty()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (_session->data().wallpapers().empty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_over = _overDown = Selection();
|
||||
|
||||
_papers = _session->data().wallpapers(
|
||||
) | ranges::views::filter([&](const Data::WallPaper &paper) {
|
||||
return (!paper.isPattern() || !paper.backgroundColors().empty())
|
||||
&& (!_forPeer
|
||||
|| (!Data::IsDefaultWallPaper(paper)
|
||||
&& (Data::IsCloudWallPaper(paper)
|
||||
|| Data::IsCustomWallPaper(paper))));
|
||||
}) | ranges::views::transform([](const Data::WallPaper &paper) {
|
||||
return Paper{ paper };
|
||||
}) | ranges::to_vector;
|
||||
pushCustomPapers();
|
||||
sortPapers();
|
||||
const auto was = base::take(_papers);
|
||||
if (forChannel()) {
|
||||
const auto now = _forPeer->wallPaper();
|
||||
const auto &list = _session->data().cloudThemes().chatThemes();
|
||||
if (list.empty()) {
|
||||
return;
|
||||
}
|
||||
using Type = Data::CloudThemeType;
|
||||
const auto type = Window::Theme::IsNightMode()
|
||||
? Type::Dark
|
||||
: Type::Light;
|
||||
_papers.reserve(list.size() + 1);
|
||||
const auto nowEmojiId = now ? now->emojiId() : QString();
|
||||
if (!now || !now->emojiId().isEmpty()) {
|
||||
_papers.push_back({ Window::Theme::Background()->paper() });
|
||||
_currentId = _papers.back().data.id();
|
||||
} else {
|
||||
_papers.push_back({ *now });
|
||||
_currentId = now->id();
|
||||
}
|
||||
for (const auto &theme : list) {
|
||||
const auto i = theme.settings.find(type);
|
||||
if (i != end(theme.settings) && i->second.paper) {
|
||||
_papers.push_back({ *i->second.paper });
|
||||
if (nowEmojiId == theme.emoticon) {
|
||||
_currentId = _papers.back().data.id();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_papers = _session->data().wallpapers(
|
||||
) | ranges::views::filter([&](const Data::WallPaper &paper) {
|
||||
return (!paper.isPattern() || !paper.backgroundColors().empty())
|
||||
&& (!_forPeer
|
||||
|| (!Data::IsDefaultWallPaper(paper)
|
||||
&& (Data::IsCloudWallPaper(paper)
|
||||
|| Data::IsCustomWallPaper(paper))));
|
||||
}) | ranges::views::transform([](const Data::WallPaper &paper) {
|
||||
return Paper{ paper };
|
||||
}) | ranges::to_vector;
|
||||
pushCustomPapers();
|
||||
sortPapers();
|
||||
}
|
||||
resizeToContentAndPreload();
|
||||
}
|
||||
|
||||
@@ -587,6 +683,10 @@ void BackgroundBox::Inner::validatePaperThumbnail(
|
||||
paper.thumbnail.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
|
||||
bool BackgroundBox::Inner::forChannel() const {
|
||||
return _forPeer && _forPeer->isChannel();
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::paintPaper(
|
||||
QPainter &p,
|
||||
const Paper &paper,
|
||||
@@ -604,7 +704,8 @@ void BackgroundBox::Inner::paintPaper(
|
||||
const auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
const auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
_check->paint(p, checkLeft, checkTop, width());
|
||||
} else if (Data::IsCloudWallPaper(paper.data)
|
||||
} else if (!forChannel()
|
||||
&& Data::IsCloudWallPaper(paper.data)
|
||||
&& !Data::IsDefaultWallPaper(paper.data)
|
||||
&& !Data::IsLegacy2DefaultWallPaper(paper.data)
|
||||
&& !Data::IsLegacy3DefaultWallPaper(paper.data)
|
||||
@@ -642,7 +743,8 @@ void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) {
|
||||
- st::stickerPanDeleteIconBg.width();
|
||||
const auto deleteBottom = row * (height + skip) + skip
|
||||
+ st::stickerPanDeleteIconBg.height();
|
||||
const auto inDelete = (x >= deleteLeft)
|
||||
const auto inDelete = !forChannel()
|
||||
&& (x >= deleteLeft)
|
||||
&& (y < deleteBottom)
|
||||
&& Data::IsCloudWallPaper(data)
|
||||
&& !Data::IsDefaultWallPaper(data)
|
||||
|
||||
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
|
||||
class PeerData;
|
||||
|
||||
@@ -38,6 +38,7 @@ private:
|
||||
const Data::WallPaper &paper) const;
|
||||
void removePaper(const Data::WallPaper &paper);
|
||||
void resetForPeer();
|
||||
[[nodiscard]] bool forChannel() const;
|
||||
|
||||
void chooseFromFile();
|
||||
|
||||
|
||||
@@ -7,10 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/background_preview_box.h"
|
||||
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/peers/edit_peer_color_box.h"
|
||||
#include "boxes/premium_preview_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/boxes/boost_box.h"
|
||||
#include "ui/controls/chat_service_checkbox.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
@@ -18,13 +22,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_session.h"
|
||||
@@ -33,17 +41,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_document_resolver.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/background_preview_box.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/themes/window_themes_embedded.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "settings/settings_premium.h"
|
||||
#include "storage/file_upload.h"
|
||||
#include "storage/localimageloader.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/themes/window_themes_embedded.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QGuiApplication>
|
||||
@@ -51,7 +57,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxWallPaperSlugLength = 255;
|
||||
constexpr auto kDefaultDimming = 50;
|
||||
|
||||
[[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) {
|
||||
if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) {
|
||||
@@ -157,6 +162,25 @@ constexpr auto kDefaultDimming = 50;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] Data::WallPaper Resolve(
|
||||
not_null<Main::Session*> session,
|
||||
const Data::WallPaper &paper,
|
||||
bool dark) {
|
||||
if (paper.emojiId().isEmpty()) {
|
||||
return paper;
|
||||
}
|
||||
const auto &themes = session->data().cloudThemes();
|
||||
if (const auto theme = themes.themeForEmoji(paper.emojiId())) {
|
||||
using Type = Data::CloudThemeType;
|
||||
const auto type = dark ? Type::Dark : Type::Light;
|
||||
const auto i = theme->settings.find(type);
|
||||
if (i != end(theme->settings) && i->second.paper) {
|
||||
return *i->second.paper;
|
||||
}
|
||||
}
|
||||
return paper;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct BackgroundPreviewBox::OverridenStyle {
|
||||
@@ -194,15 +218,17 @@ BackgroundPreviewBox::BackgroundPreviewBox(
|
||||
? tr::lng_background_apply2(tr::now)
|
||||
: tr::lng_background_text2(tr::now)),
|
||||
true))
|
||||
, _paper(paper)
|
||||
, _paperEmojiId(paper.emojiId())
|
||||
, _paper(
|
||||
Resolve(&controller->session(), paper, Window::Theme::IsNightMode()))
|
||||
, _media(_paper.document() ? _paper.document()->createMediaView() : nullptr)
|
||||
, _radial([=](crl::time now) { radialAnimationCallback(now); })
|
||||
, _appNightMode(Window::Theme::IsNightModeValue())
|
||||
, _boxDarkMode(_appNightMode.current())
|
||||
, _dimmingIntensity(std::clamp(paper.patternIntensity(), 0, 100))
|
||||
, _dimmingIntensity(std::clamp(_paper.patternIntensity(), 0, 100))
|
||||
, _dimmed(_forPeer
|
||||
&& (paper.document() || paper.localThumbnail())
|
||||
&& !paper.isPattern()) {
|
||||
&& (_paper.document() || _paper.localThumbnail())
|
||||
&& !_paper.isPattern()) {
|
||||
if (_media) {
|
||||
_media->thumbnailWanted(_paper.fileOrigin());
|
||||
}
|
||||
@@ -242,7 +268,36 @@ BackgroundPreviewBox::BackgroundPreviewBox(
|
||||
|
||||
BackgroundPreviewBox::~BackgroundPreviewBox() = default;
|
||||
|
||||
void BackgroundPreviewBox::recreate(bool dark) {
|
||||
_paper = Resolve(
|
||||
&_controller->session(),
|
||||
Data::WallPaper::FromEmojiId(_paperEmojiId),
|
||||
dark);
|
||||
_media = _paper.document()
|
||||
? _paper.document()->createMediaView()
|
||||
: nullptr;
|
||||
if (_media) {
|
||||
_media->thumbnailWanted(_paper.fileOrigin());
|
||||
}
|
||||
_full = QImage();
|
||||
_generated = _scaled = _blurred = _fadeOutThumbnail = QPixmap();
|
||||
_generating = {};
|
||||
generateBackground();
|
||||
_paper.loadDocument();
|
||||
if (const auto document = _paper.document()) {
|
||||
if (document->loading()) {
|
||||
_radial.start(_media->progress());
|
||||
}
|
||||
}
|
||||
checkLoadedDocument();
|
||||
updateServiceBg(_paper.backgroundColors());
|
||||
update();
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::applyDarkMode(bool dark) {
|
||||
if (!_paperEmojiId.isEmpty()) {
|
||||
recreate(dark);
|
||||
}
|
||||
const auto equals = (dark == Window::Theme::IsNightMode());
|
||||
const auto &palette = (dark ? _darkPalette : _lightPalette);
|
||||
if (!equals && !palette) {
|
||||
@@ -293,10 +348,10 @@ void BackgroundPreviewBox::createDimmingSlider(bool dark) {
|
||||
const auto equals = (dark == Window::Theme::IsNightMode());
|
||||
const auto inner = Ui::CreateChild<Ui::VerticalLayout>(_dimmingContent);
|
||||
inner->show();
|
||||
Settings::AddSubsectionTitle(
|
||||
Ui::AddSubsectionTitle(
|
||||
inner,
|
||||
tr::lng_background_dimming(),
|
||||
style::margins(0, st::settingsSectionSkip, 0, 0),
|
||||
style::margins(0, st::defaultVerticalListSkip, 0, 0),
|
||||
equals ? nullptr : dark ? &_dark->subtitle : &_light->subtitle);
|
||||
_dimmingSlider = inner->add(
|
||||
object_ptr<Ui::MediaSlider>(
|
||||
@@ -378,7 +433,7 @@ auto BackgroundPreviewBox::prepareOverridenStyle(bool dark)
|
||||
.box = st::defaultBox,
|
||||
.toggle = toggle,
|
||||
.slider = st::defaultContinuousSlider,
|
||||
.subtitle = st::settingsSubsectionTitle,
|
||||
.subtitle = st::defaultSubsectionTitle,
|
||||
};
|
||||
result.box.button.textFg = p->lightButtonFg();
|
||||
result.box.button.textFgOver = p->lightButtonFgOver();
|
||||
@@ -408,6 +463,10 @@ auto BackgroundPreviewBox::prepareOverridenStyle(bool dark)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BackgroundPreviewBox::forChannel() const {
|
||||
return _forPeer && _forPeer->isChannel();
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::generateBackground() {
|
||||
if (_paper.backgroundColors().empty()) {
|
||||
return;
|
||||
@@ -433,7 +492,9 @@ void BackgroundPreviewBox::resetTitle() {
|
||||
|
||||
void BackgroundPreviewBox::rebuildButtons(bool dark) {
|
||||
clearButtons();
|
||||
addButton(_forPeer
|
||||
addButton(forChannel()
|
||||
? tr::lng_background_apply_channel()
|
||||
: _forPeer
|
||||
? tr::lng_background_apply_button()
|
||||
: tr::lng_settings_apply(), [=] { apply(); });
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
@@ -510,6 +571,10 @@ void BackgroundPreviewBox::recreateBlurCheckbox() {
|
||||
}, _blur->lifetime());
|
||||
|
||||
_blur->setDisabled(_paper.document() && _full.isNull());
|
||||
|
||||
if (_forBothOverlay) {
|
||||
_forBothOverlay->raise();
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::apply() {
|
||||
@@ -520,7 +585,7 @@ void BackgroundPreviewBox::apply() {
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::uploadForPeer() {
|
||||
void BackgroundPreviewBox::uploadForPeer(bool both) {
|
||||
Expects(_forPeer != nullptr);
|
||||
|
||||
if (_uploadId) {
|
||||
@@ -579,7 +644,7 @@ void BackgroundPreviewBox::uploadForPeer() {
|
||||
"Got wallPaperNoFile after account.UploadWallPaper."));
|
||||
});
|
||||
if (const auto paper = Data::WallPaper::Create(session, result)) {
|
||||
setExistingForPeer(*paper);
|
||||
setExistingForPeer(*paper, both);
|
||||
}
|
||||
}).send();
|
||||
}, _uploadLifetime);
|
||||
@@ -588,7 +653,9 @@ void BackgroundPreviewBox::uploadForPeer() {
|
||||
_radial.start(_uploadProgress);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::setExistingForPeer(const Data::WallPaper &paper) {
|
||||
void BackgroundPreviewBox::setExistingForPeer(
|
||||
const Data::WallPaper &paper,
|
||||
bool both) {
|
||||
Expects(_forPeer != nullptr);
|
||||
|
||||
if (const auto already = _forPeer->wallPaper()) {
|
||||
@@ -602,6 +669,7 @@ void BackgroundPreviewBox::setExistingForPeer(const Data::WallPaper &paper) {
|
||||
api->request(MTPmessages_SetChatWallPaper(
|
||||
MTP_flags((_fromMessageId ? Flag::f_id : Flag())
|
||||
| (_fromMessageId ? Flag() : Flag::f_wallpaper)
|
||||
| (both ? Flag::f_for_both : Flag())
|
||||
| Flag::f_settings),
|
||||
_forPeer->input,
|
||||
paper.mtpInput(&_controller->session()),
|
||||
@@ -615,13 +683,155 @@ void BackgroundPreviewBox::setExistingForPeer(const Data::WallPaper &paper) {
|
||||
_controller->finishChatThemeEdit(_forPeer);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::checkLevelForChannel() {
|
||||
Expects(forChannel());
|
||||
|
||||
const auto show = _controller->uiShow();
|
||||
_forPeerLevelCheck = true;
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
CheckBoostLevel(show, _forPeer, [=](int level) {
|
||||
if (!weak) {
|
||||
return std::optional<Ui::AskBoostReason>();
|
||||
}
|
||||
const auto appConfig = &_forPeer->session().account().appConfig();
|
||||
const auto defaultRequired = appConfig->get<int>(
|
||||
"channel_wallpaper_level_min",
|
||||
9);
|
||||
const auto customRequired = appConfig->get<int>(
|
||||
"channel_custom_wallpaper_level_min",
|
||||
10);
|
||||
const auto required = _paperEmojiId.isEmpty()
|
||||
? customRequired
|
||||
: defaultRequired;
|
||||
if (level >= required) {
|
||||
applyForPeer(false);
|
||||
return std::optional<Ui::AskBoostReason>();
|
||||
}
|
||||
return std::make_optional(Ui::AskBoostReason{
|
||||
Ui::AskBoostWallpaper{ required }
|
||||
});
|
||||
}, [=] { _forPeerLevelCheck = false; });
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::applyForPeer() {
|
||||
Expects(_forPeer != nullptr);
|
||||
|
||||
if (Data::IsCustomWallPaper(_paper)) {
|
||||
uploadForPeer();
|
||||
if (!Data::IsCustomWallPaper(_paper)) {
|
||||
if (const auto already = _forPeer->wallPaper()) {
|
||||
if (already->equals(_paper)) {
|
||||
_controller->finishChatThemeEdit(_forPeer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (forChannel()) {
|
||||
checkLevelForChannel();
|
||||
return;
|
||||
} else if (_fromMessageId || !_forPeer->session().premiumPossible()) {
|
||||
applyForPeer(false);
|
||||
return;
|
||||
} else if (_forBothOverlay) {
|
||||
return;
|
||||
}
|
||||
const auto size = this->size() * style::DevicePixelRatio();
|
||||
const auto bg = Images::DitherImage(
|
||||
Images::BlurLargeImage(
|
||||
Ui::GrabWidgetToImage(this).scaled(
|
||||
size / style::ConvertScale(4),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation),
|
||||
24).scaled(
|
||||
size,
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation));
|
||||
|
||||
_forBothOverlay = std::make_unique<Ui::FadeWrap<>>(
|
||||
this,
|
||||
object_ptr<Ui::RpWidget>(this));
|
||||
const auto overlay = _forBothOverlay->entity();
|
||||
|
||||
sizeValue() | rpl::start_with_next([=](QSize size) {
|
||||
_forBothOverlay->setGeometry({ QPoint(), size });
|
||||
overlay->setGeometry({ QPoint(), size });
|
||||
}, _forBothOverlay->lifetime());
|
||||
|
||||
overlay->paintRequest(
|
||||
) | rpl::start_with_next([=](QRect clip) {
|
||||
auto p = QPainter(overlay);
|
||||
p.drawImage(0, 0, bg);
|
||||
p.fillRect(clip, QColor(0, 0, 0, 64));
|
||||
}, overlay->lifetime());
|
||||
|
||||
using namespace Ui;
|
||||
const auto forMe = CreateChild<RoundButton>(
|
||||
overlay,
|
||||
tr::lng_background_apply_me(),
|
||||
st::backgroundConfirm);
|
||||
forMe->setClickedCallback([=] {
|
||||
applyForPeer(false);
|
||||
});
|
||||
using namespace rpl::mappers;
|
||||
const auto forBoth = ::Settings::CreateLockedButton(
|
||||
overlay,
|
||||
tr::lng_background_apply_both(
|
||||
lt_user,
|
||||
rpl::single(_forPeer->shortName())),
|
||||
st::backgroundConfirm,
|
||||
Data::AmPremiumValue(&_forPeer->session()) | rpl::map(!_1));
|
||||
forBoth->setClickedCallback([=] {
|
||||
if (_forPeer->session().premium()) {
|
||||
applyForPeer(true);
|
||||
} else {
|
||||
ShowPremiumPreviewBox(
|
||||
_controller->uiShow(),
|
||||
PremiumPreview::Wallpapers);
|
||||
}
|
||||
});
|
||||
const auto cancel = CreateChild<RoundButton>(
|
||||
overlay,
|
||||
tr::lng_cancel(),
|
||||
st::backgroundConfirmCancel);
|
||||
cancel->setClickedCallback([=] {
|
||||
const auto raw = _forBothOverlay.release();
|
||||
raw->shownValue() | rpl::filter(
|
||||
!rpl::mappers::_1
|
||||
) | rpl::take(1) | rpl::start_with_next(crl::guard(raw, [=] {
|
||||
delete raw;
|
||||
}), raw->lifetime());
|
||||
raw->toggle(false, anim::type::normal);
|
||||
});
|
||||
forMe->setTextTransform(RoundButton::TextTransform::NoTransform);
|
||||
forBoth->setTextTransform(RoundButton::TextTransform::NoTransform);
|
||||
cancel->setTextTransform(RoundButton::TextTransform::NoTransform);
|
||||
|
||||
overlay->sizeValue(
|
||||
) | rpl::start_with_next([=](QSize size) {
|
||||
const auto padding = st::backgroundConfirmPadding;
|
||||
const auto width = size.width()
|
||||
- padding.left()
|
||||
- padding.right();
|
||||
const auto height = cancel->height();
|
||||
auto top = size.height() - padding.bottom() - height;
|
||||
cancel->setGeometry(padding.left(), top, width, height);
|
||||
top -= height + padding.top();
|
||||
forBoth->setGeometry(padding.left(), top, width, height);
|
||||
top -= height + padding.top();
|
||||
forMe->setGeometry(padding.left(), top, width, height);
|
||||
}, _forBothOverlay->lifetime());
|
||||
|
||||
_forBothOverlay->hide(anim::type::instant);
|
||||
_forBothOverlay->show(anim::type::normal);
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::applyForPeer(bool both) {
|
||||
using namespace Data;
|
||||
if (forChannel() && !_paperEmojiId.isEmpty()) {
|
||||
setExistingForPeer(WallPaper::FromEmojiId(_paperEmojiId), both);
|
||||
} else if (IsCustomWallPaper(_paper)) {
|
||||
uploadForPeer(both);
|
||||
} else {
|
||||
setExistingForPeer(_paper);
|
||||
setExistingForPeer(_paper, both);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -739,7 +949,7 @@ int BackgroundPreviewBox::textsTop() const {
|
||||
- st::historyPaddingBottom
|
||||
- (_service ? _service->height() : 0)
|
||||
- _text1->height()
|
||||
- _text2->height();
|
||||
- (forChannel() ? _text2->height() : 0);
|
||||
}
|
||||
|
||||
QRect BackgroundPreviewBox::radialRect() const {
|
||||
@@ -769,10 +979,11 @@ void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
|
||||
context.outbg = _text1->hasOutLayout();
|
||||
_text1->draw(p, context);
|
||||
p.translate(0, height1);
|
||||
|
||||
context.outbg = _text2->hasOutLayout();
|
||||
_text2->draw(p, context);
|
||||
p.translate(0, height2);
|
||||
if (!forChannel()) {
|
||||
context.outbg = _text2->hasOutLayout();
|
||||
_text2->draw(p, context);
|
||||
p.translate(0, height2);
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::radialAnimationCallback(crl::time now) {
|
||||
@@ -872,7 +1083,9 @@ void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
|
||||
_service = GenerateServiceItem(
|
||||
delegate(),
|
||||
_serviceHistory,
|
||||
((_forPeer && !_fromMessageId)
|
||||
(forChannel()
|
||||
? tr::lng_background_other_channel(tr::now)
|
||||
: (_forPeer && !_fromMessageId)
|
||||
? tr::lng_background_other_info(
|
||||
tr::now,
|
||||
lt_user,
|
||||
|
||||
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
#include "base/binary_guard.h"
|
||||
#include "history/admin_log/history_admin_log_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
@@ -29,6 +29,8 @@ class ChatStyle;
|
||||
class MediaSlider;
|
||||
template <typename Widget>
|
||||
class SlideWrap;
|
||||
template <typename Widget>
|
||||
class FadeWrap;
|
||||
} // namespace Ui
|
||||
|
||||
struct BackgroundPreviewArgs {
|
||||
@@ -66,9 +68,10 @@ private:
|
||||
|
||||
void apply();
|
||||
void applyForPeer();
|
||||
void applyForPeer(bool both);
|
||||
void applyForEveryone();
|
||||
void uploadForPeer();
|
||||
void setExistingForPeer(const Data::WallPaper &paper);
|
||||
void uploadForPeer(bool both);
|
||||
void setExistingForPeer(const Data::WallPaper &paper, bool both);
|
||||
void share();
|
||||
void radialAnimationCallback(crl::time now);
|
||||
QRect radialRect() const;
|
||||
@@ -91,18 +94,24 @@ private:
|
||||
void applyDarkMode(bool dark);
|
||||
[[nodiscard]] OverridenStyle prepareOverridenStyle(bool dark);
|
||||
|
||||
[[nodiscard]] bool forChannel() const;
|
||||
void checkLevelForChannel();
|
||||
|
||||
void recreate(bool dark);
|
||||
void resetTitle();
|
||||
void rebuildButtons(bool dark);
|
||||
void createDimmingSlider(bool dark);
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
PeerData * const _forPeer = nullptr;
|
||||
bool _forPeerLevelCheck = false;
|
||||
FullMsgId _fromMessageId;
|
||||
std::unique_ptr<Ui::ChatStyle> _chatStyle;
|
||||
const not_null<History*> _serviceHistory;
|
||||
AdminLog::OwnedItem _service;
|
||||
AdminLog::OwnedItem _text1;
|
||||
AdminLog::OwnedItem _text2;
|
||||
QString _paperEmojiId;
|
||||
Data::WallPaper _paper;
|
||||
std::shared_ptr<Data::DocumentMedia> _media;
|
||||
QImage _full;
|
||||
@@ -131,6 +140,8 @@ private:
|
||||
float64 _uploadProgress = 0.;
|
||||
rpl::lifetime _uploadLifetime;
|
||||
|
||||
std::unique_ptr<Ui::FadeWrap<Ui::RpWidget>> _forBothOverlay;
|
||||
|
||||
rpl::variable<QColor> _paletteServiceBg;
|
||||
rpl::lifetime _serviceBgLifetime;
|
||||
|
||||
|
||||
@@ -753,7 +753,28 @@ backgroundCheck: ServiceCheck {
|
||||
color: msgServiceFg;
|
||||
duration: 200;
|
||||
}
|
||||
backgroundConfirmPadding: margins(24px, 16px, 24px, 16px);
|
||||
backgroundConfirm: RoundButton(defaultActiveButton) {
|
||||
height: 44px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
}
|
||||
backgroundConfirmCancel: RoundButton(backgroundConfirm) {
|
||||
textFg: mediaviewSaveMsgFg;
|
||||
textFgOver: mediaviewSaveMsgFg;
|
||||
numbersTextFg: mediaviewSaveMsgFg;
|
||||
numbersTextFgOver: mediaviewSaveMsgFg;
|
||||
textBg: shadowFg;
|
||||
textBgOver: shadowFg;
|
||||
|
||||
height: 44px;
|
||||
textTop: 12px;
|
||||
font: font(13px semibold);
|
||||
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: shadowFg;
|
||||
}
|
||||
}
|
||||
urlAuthCheckbox: Checkbox(defaultBoxCheckbox) {
|
||||
width: 240px;
|
||||
}
|
||||
@@ -906,6 +927,14 @@ sponsoredUrlButton: RoundButton(defaultActiveButton) {
|
||||
}
|
||||
}
|
||||
|
||||
requestPeerRestriction: FlatLabel(defaultFlatLabel) {
|
||||
minWidth: 240px;
|
||||
textFg: membersAboutLimitFg;
|
||||
style: TextStyle(boxTextStyle) {
|
||||
lineHeight: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
requestsBoxItem: PeerListItem(peerListBoxItem) {
|
||||
height: 99px;
|
||||
button: OutlineButton(defaultPeerListButton) {
|
||||
|
||||
@@ -113,7 +113,8 @@ Base64UrlInput::Base64UrlInput(
|
||||
rpl::producer<QString> placeholder,
|
||||
const QString &val)
|
||||
: MaskedInputField(parent, st, std::move(placeholder), val) {
|
||||
if (!QRegularExpression("^[a-zA-Z0-9_\\-]+$").match(val).hasMatch()) {
|
||||
static const auto RegExp = QRegularExpression("^[a-zA-Z0-9_\\-]+$");
|
||||
if (!RegExp.match(val).hasMatch()) {
|
||||
setText(QString());
|
||||
}
|
||||
}
|
||||
@@ -831,8 +832,9 @@ void ProxyBox::prepare() {
|
||||
connect(_host.data(), &HostInput::changed, [=] {
|
||||
Ui::PostponeCall(_host, [=] {
|
||||
const auto host = _host->getLastText().trimmed();
|
||||
static const auto mask = u"^\\d+\\.\\d+\\.\\d+\\.\\d+:(\\d*)$"_q;
|
||||
const auto match = QRegularExpression(mask).match(host);
|
||||
static const auto mask = QRegularExpression(
|
||||
u"^\\d+\\.\\d+\\.\\d+\\.\\d+:(\\d*)$"_q);
|
||||
const auto match = mask.match(host);
|
||||
if (_host->cursorPosition() == host.size()
|
||||
&& match.hasMatch()) {
|
||||
const auto port = match.captured(1);
|
||||
@@ -1107,6 +1109,10 @@ void ProxiesBoxController::ShowApplyConfirmation(
|
||||
proxy.password = fields.value(u"secret"_q);
|
||||
}
|
||||
if (proxy) {
|
||||
static const auto UrlStartRegExp = QRegularExpression(
|
||||
"^https://",
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
static const auto UrlEndRegExp = QRegularExpression("/$");
|
||||
const auto displayed = "https://" + server + "/";
|
||||
const auto parsed = QUrl::fromUserInput(displayed);
|
||||
const auto displayUrl = !UrlClickHandler::IsSuspicious(displayed)
|
||||
@@ -1117,11 +1123,9 @@ void ProxiesBoxController::ShowApplyConfirmation(
|
||||
const auto displayServer = QString(
|
||||
displayUrl
|
||||
).replace(
|
||||
QRegularExpression(
|
||||
"^https://",
|
||||
QRegularExpression::CaseInsensitiveOption),
|
||||
UrlStartRegExp,
|
||||
QString()
|
||||
).replace(QRegularExpression("/$"), QString());
|
||||
).replace(UrlEndRegExp, QString());
|
||||
const auto text = tr::lng_sure_enable_socks(
|
||||
tr::now,
|
||||
lt_server,
|
||||
|
||||
@@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "base/timer.h"
|
||||
#include "base/object_ptr.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "core/core_settings_proxy.h"
|
||||
#include "mtproto/connection_abstract.h"
|
||||
#include "mtproto/mtproto_proxy_data.h"
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "main/main_session.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
@@ -26,7 +27,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "menu/menu_send.h"
|
||||
#include "history/view/history_view_schedule_box.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "base/unique_qptr.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "base/call_delayed.h"
|
||||
@@ -34,7 +34,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -790,7 +789,7 @@ not_null<Ui::InputField*> CreatePollBox::setupQuestion(
|
||||
using namespace Settings;
|
||||
|
||||
const auto session = &_controller->session();
|
||||
AddSubsectionTitle(container, tr::lng_polls_create_question());
|
||||
Ui::AddSubsectionTitle(container, tr::lng_polls_create_question());
|
||||
const auto question = container->add(
|
||||
object_ptr<Ui::InputField>(
|
||||
container,
|
||||
@@ -818,9 +817,9 @@ not_null<Ui::InputField*> CreatePollBox::setupQuestion(
|
||||
- st::createPollWarningPosition.x()),
|
||||
(geometry.y()
|
||||
- st::createPollFieldPadding.top()
|
||||
- st::settingsSubsectionTitlePadding.bottom()
|
||||
- st::settingsSubsectionTitle.style.font->height
|
||||
+ st::settingsSubsectionTitle.style.font->ascent
|
||||
- st::defaultSubsectionTitlePadding.bottom()
|
||||
- st::defaultSubsectionTitle.style.font->height
|
||||
+ st::defaultSubsectionTitle.style.font->ascent
|
||||
- st::createPollWarning.style.font->ascent),
|
||||
geometry.width());
|
||||
}, warning->lifetime());
|
||||
@@ -841,8 +840,8 @@ not_null<Ui::InputField*> CreatePollBox::setupSolution(
|
||||
const auto inner = outer->entity();
|
||||
|
||||
const auto session = &_controller->session();
|
||||
AddSkip(inner);
|
||||
AddSubsectionTitle(inner, tr::lng_polls_solution_title());
|
||||
Ui::AddSkip(inner);
|
||||
Ui::AddSubsectionTitle(inner, tr::lng_polls_solution_title());
|
||||
const auto solution = inner->add(
|
||||
object_ptr<Ui::InputField>(
|
||||
inner,
|
||||
@@ -875,9 +874,9 @@ not_null<Ui::InputField*> CreatePollBox::setupSolution(
|
||||
- st::createPollWarningPosition.x()),
|
||||
(geometry.y()
|
||||
- st::createPollFieldPadding.top()
|
||||
- st::settingsSubsectionTitlePadding.bottom()
|
||||
- st::settingsSubsectionTitle.style.font->height
|
||||
+ st::settingsSubsectionTitle.style.font->ascent
|
||||
- st::defaultSubsectionTitlePadding.bottom()
|
||||
- st::defaultSubsectionTitle.style.font->height
|
||||
+ st::defaultSubsectionTitle.style.font->ascent
|
||||
- st::createPollWarning.style.font->ascent),
|
||||
geometry.width());
|
||||
}, warning->lifetime());
|
||||
@@ -902,13 +901,13 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
const auto container = result.data();
|
||||
|
||||
const auto question = setupQuestion(container);
|
||||
AddDivider(container);
|
||||
AddSkip(container);
|
||||
Ui::AddDivider(container);
|
||||
Ui::AddSkip(container);
|
||||
container->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
tr::lng_polls_create_options(),
|
||||
st::settingsSubsectionTitle),
|
||||
st::defaultSubsectionTitle),
|
||||
st::createPollFieldTitlePadding);
|
||||
const auto options = lifetime().make_state<Options>(
|
||||
getDelegate()->outerContainer(),
|
||||
@@ -939,8 +938,8 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
options->focusFirst();
|
||||
}, question->lifetime());
|
||||
|
||||
AddSkip(container);
|
||||
AddSubsectionTitle(container, tr::lng_polls_create_settings());
|
||||
Ui::AddSkip(container);
|
||||
Ui::AddSubsectionTitle(container, tr::lng_polls_create_settings());
|
||||
|
||||
const auto anonymous = (!(_disabled & PollData::Flag::PublicVotes))
|
||||
? container->add(
|
||||
|
||||
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
#include "api/api_common.h"
|
||||
#include "data/data_poll.h"
|
||||
#include "base/flags.h"
|
||||
|
||||
@@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_SPELLCHECK
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
|
||||
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
|
||||
namespace ChatHelpers {
|
||||
|
||||
@@ -13,9 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "history/history.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "settings/settings_privacy_security.h"
|
||||
#include "calls/calls_instance.h"
|
||||
#include "base/binary_guard.h"
|
||||
@@ -233,7 +233,7 @@ Ui::FlatLabel *EditPrivacyBox::addLabel(
|
||||
object_ptr<Ui::DividerLabel>(
|
||||
container,
|
||||
std::move(label),
|
||||
st::settingsDividerLabelPadding),
|
||||
st::defaultBoxDividerLabelPadding),
|
||||
{ 0, topSkip, 0, 0 });
|
||||
return result;
|
||||
}
|
||||
@@ -294,7 +294,7 @@ void EditPrivacyBox::setupContent() {
|
||||
const auto button = content->add(
|
||||
object_ptr<Ui::SlideWrap<Button>>(
|
||||
content,
|
||||
CreateButton(
|
||||
object_ptr<Button>(
|
||||
content,
|
||||
rpl::duplicate(text),
|
||||
st::settingsButtonNoIcon)));
|
||||
@@ -321,7 +321,7 @@ void EditPrivacyBox::setupContent() {
|
||||
content->add(std::move(above));
|
||||
}
|
||||
|
||||
AddSubsectionTitle(
|
||||
Ui::AddSubsectionTitle(
|
||||
content,
|
||||
_controller->optionsTitleKey(),
|
||||
{ 0, st::settingsPrivacySkipTop, 0, 0 });
|
||||
@@ -332,7 +332,7 @@ void EditPrivacyBox::setupContent() {
|
||||
const auto warning = addLabelOrDivider(
|
||||
content,
|
||||
_controller->warning(),
|
||||
st::settingsSectionSkip + st::settingsPrivacySkipTop);
|
||||
st::defaultVerticalListSkip + st::settingsPrivacySkipTop);
|
||||
if (warning) {
|
||||
_controller->prepareWarningLabel(warning);
|
||||
}
|
||||
@@ -345,8 +345,8 @@ void EditPrivacyBox::setupContent() {
|
||||
content->add(std::move(middle));
|
||||
}
|
||||
|
||||
AddSkip(content);
|
||||
AddSubsectionTitle(
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSubsectionTitle(
|
||||
content,
|
||||
tr::lng_edit_privacy_exceptions(),
|
||||
{ 0, st::settingsPrivacySkipTop, 0, 0 });
|
||||
@@ -355,7 +355,7 @@ void EditPrivacyBox::setupContent() {
|
||||
addLabel(
|
||||
content,
|
||||
_controller->exceptionsDescription() | Ui::Text::ToWithEntities(),
|
||||
st::settingsSectionSkip);
|
||||
st::defaultVerticalListSkip);
|
||||
|
||||
if (auto below = _controller->setupBelowWidget(_window, content)) {
|
||||
content->add(std::move(below));
|
||||
|
||||
@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/filter_icons.h"
|
||||
#include "ui/filter_icon_panel.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_peer.h"
|
||||
@@ -515,7 +516,7 @@ not_null<Ui::SettingsButton*> AddToggledButton(
|
||||
const auto toggled = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
|
||||
container,
|
||||
CreateButton(
|
||||
CreateButtonWithIcon(
|
||||
container,
|
||||
std::move(text),
|
||||
st,
|
||||
@@ -658,12 +659,12 @@ void EditFilterBox(
|
||||
name->setFocusFast();
|
||||
});
|
||||
|
||||
AddSkip(content);
|
||||
AddDivider(content);
|
||||
AddSkip(content);
|
||||
AddSubsectionTitle(content, tr::lng_filters_include());
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddDivider(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSubsectionTitle(content, tr::lng_filters_include());
|
||||
|
||||
const auto includeAdd = AddButton(
|
||||
const auto includeAdd = AddButtonWithIcon(
|
||||
content,
|
||||
tr::lng_filters_add_chats(),
|
||||
st::settingsButtonActive,
|
||||
@@ -676,9 +677,9 @@ void EditFilterBox(
|
||||
kTypes,
|
||||
&Data::ChatFilter::always);
|
||||
|
||||
AddSkip(content);
|
||||
AddDividerText(content, tr::lng_filters_include_about());
|
||||
AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddDividerText(content, tr::lng_filters_include_about());
|
||||
Ui::AddSkip(content);
|
||||
|
||||
auto excludeWrap = content->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
@@ -688,9 +689,9 @@ void EditFilterBox(
|
||||
excludeWrap->toggleOn(state->chatlist.value() | rpl::map(!_1));
|
||||
const auto excludeInner = excludeWrap->entity();
|
||||
|
||||
AddSubsectionTitle(excludeInner, tr::lng_filters_exclude());
|
||||
Ui::AddSubsectionTitle(excludeInner, tr::lng_filters_exclude());
|
||||
|
||||
const auto excludeAdd = AddButton(
|
||||
const auto excludeAdd = AddButtonWithIcon(
|
||||
excludeInner,
|
||||
tr::lng_filters_remove_chats(),
|
||||
st::settingsButtonActive,
|
||||
@@ -703,9 +704,9 @@ void EditFilterBox(
|
||||
kExcludeTypes,
|
||||
&Data::ChatFilter::never);
|
||||
|
||||
AddSkip(excludeInner);
|
||||
AddDividerText(excludeInner, tr::lng_filters_exclude_about());
|
||||
AddSkip(excludeInner);
|
||||
Ui::AddSkip(excludeInner);
|
||||
Ui::AddDividerText(excludeInner, tr::lng_filters_exclude_about());
|
||||
Ui::AddSkip(excludeInner);
|
||||
|
||||
const auto collect = [=]() -> std::optional<Data::ChatFilter> {
|
||||
const auto title = name->getLastText().trimmed();
|
||||
@@ -726,7 +727,7 @@ void EditFilterBox(
|
||||
return rules.withTitle(title);
|
||||
};
|
||||
|
||||
AddSubsectionTitle(
|
||||
Ui::AddSubsectionTitle(
|
||||
content,
|
||||
rpl::conditional(
|
||||
state->hasLinks.value(),
|
||||
@@ -806,8 +807,8 @@ void EditFilterBox(
|
||||
}));
|
||||
}));
|
||||
}, createLink->lifetime());
|
||||
AddSkip(content);
|
||||
AddDividerText(
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddDividerText(
|
||||
content,
|
||||
rpl::conditional(
|
||||
state->hasLinks.value(),
|
||||
|
||||
@@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_layers.h"
|
||||
@@ -140,12 +141,12 @@ void ChatFilterLinkBox(
|
||||
tr::lng_group_invite_label_header(),
|
||||
data.title),
|
||||
style::margins(
|
||||
st::settingsSubsectionTitlePadding.left(),
|
||||
st::settingsSectionSkip,
|
||||
st::settingsSubsectionTitlePadding.right(),
|
||||
st::settingsSectionSkip * 2));
|
||||
st::defaultSubsectionTitlePadding.left(),
|
||||
st::defaultVerticalListSkip,
|
||||
st::defaultSubsectionTitlePadding.right(),
|
||||
st::defaultVerticalListSkip * 2));
|
||||
labelField->setMaxLength(kMaxLinkTitleLength);
|
||||
Settings::AddDivider(container);
|
||||
AddDivider(container);
|
||||
|
||||
box->setFocusCallback([=] {
|
||||
labelField->setFocusFast();
|
||||
@@ -616,7 +617,7 @@ void LinkController::addLinkBlock(not_null<Ui::VerticalLayout*> container) {
|
||||
&st::menuIconDelete);
|
||||
return result;
|
||||
};
|
||||
AddSubsectionTitle(
|
||||
Ui::AddSubsectionTitle(
|
||||
container,
|
||||
tr::lng_filters_link_subtitle(),
|
||||
st::filterLinkSubsectionTitlePadding);
|
||||
@@ -637,11 +638,11 @@ void LinkController::addLinkBlock(not_null<Ui::VerticalLayout*> container) {
|
||||
|
||||
AddCopyShareLinkButtons(container, copyLink, shareLink);
|
||||
|
||||
AddSkip(container, st::inviteLinkJoinedRowPadding.bottom() * 2);
|
||||
Ui::AddSkip(container, st::inviteLinkJoinedRowPadding.bottom() * 2);
|
||||
|
||||
AddSkip(container);
|
||||
Ui::AddSkip(container);
|
||||
|
||||
AddDivider(container);
|
||||
Ui::AddDivider(container);
|
||||
}
|
||||
|
||||
void LinkController::prepare() {
|
||||
@@ -788,7 +789,7 @@ void LinkController::setupBelowWidget() {
|
||||
? tr::lng_filters_link_chats_no_about()
|
||||
: tr::lng_filters_link_chats_about()),
|
||||
st::boxDividerLabel),
|
||||
st::settingsDividerLabelPadding));
|
||||
st::defaultBoxDividerLabelPadding));
|
||||
}
|
||||
|
||||
Main::Session &LinkController::session() const {
|
||||
@@ -1152,7 +1153,7 @@ void AddFilterSubtitleWithToggles(
|
||||
font->width(tr::lng_filters_by_link_select(tr::now)),
|
||||
font->width(tr::lng_filters_by_link_deselect(tr::now))));
|
||||
}
|
||||
const auto title = Settings::AddSubsectionTitle(
|
||||
const auto title = Ui::AddSubsectionTitle(
|
||||
container,
|
||||
std::move(text),
|
||||
padding);
|
||||
|
||||
@@ -12,18 +12,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "apiwrap.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "base/weak_ptr.h"
|
||||
#include "boxes/peer_list_controllers.h" // ContactsBoxController.
|
||||
#include "boxes/peers/prepare_short_info_box.h"
|
||||
#include "boxes/peers/replace_boost_box.h" // BoostsForGift.
|
||||
#include "boxes/premium_preview_box.h" // ShowPremiumPreviewBox.
|
||||
#include "core/ui_integration.h" // Core::MarkedTextContext.
|
||||
#include "data/data_boosts.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_media_types.h" // Data::Giveaway
|
||||
#include "data/data_media_types.h" // Data::GiveawayStart.
|
||||
#include "data/data_peer_values.h" // Data::PeerPremiumValue.
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_subscription_option.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "info/boosts/giveaway/boost_badge.h" // InfiniteRadialAnimationWidget.
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "payments/payments_checkout_process.h"
|
||||
#include "payments/payments_form.h"
|
||||
#include "settings/settings_premium.h"
|
||||
#include "ui/basic_click_handlers.h" // UrlClickHandler::Open.
|
||||
#include "ui/boxes/boost_box.h" // StartFireworks.
|
||||
@@ -33,11 +41,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/effects/premium_top_bar.h"
|
||||
#include "ui/effects/spoiler_mess.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/gradient_round_button.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/table_layout.h"
|
||||
#include "window/window_peer_menu.h" // ShowChooseRecipientBox.
|
||||
#include "window/window_session_controller.h"
|
||||
@@ -51,7 +62,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kDiscountDivider = 5.;
|
||||
constexpr auto kUserpicsMax = size_t(3);
|
||||
|
||||
using GiftOption = Data::SubscriptionOption;
|
||||
using GiftOptions = Data::SubscriptionOptions;
|
||||
@@ -72,6 +83,137 @@ GiftOptions GiftOptionFromTL(const MTPDuserFull &data) {
|
||||
return result;
|
||||
}
|
||||
|
||||
using TagUser1 = lngtag_user;
|
||||
using TagUser2 = lngtag_second_user;
|
||||
using TagUser3 = lngtag_name;
|
||||
[[nodiscard]] rpl::producer<TextWithEntities> ComplexAboutLabel(
|
||||
const std::vector<not_null<UserData*>> &users,
|
||||
tr::phrase<TagUser1> phrase1,
|
||||
tr::phrase<TagUser1, TagUser2> phrase2,
|
||||
tr::phrase<TagUser1, TagUser2, TagUser3> phrase3,
|
||||
tr::phrase<lngtag_count, TagUser1, TagUser2, TagUser3> phraseMore) {
|
||||
Expects(!users.empty());
|
||||
|
||||
const auto count = users.size();
|
||||
const auto nameValue = [&](not_null<UserData*> user) {
|
||||
return user->session().changes().peerFlagsValue(
|
||||
user,
|
||||
Data::PeerUpdate::Flag::Name
|
||||
) | rpl::map([=] { return TextWithEntities{ user->firstName }; });
|
||||
};
|
||||
if (count == 1) {
|
||||
return phrase1(
|
||||
lt_user,
|
||||
nameValue(users.front()),
|
||||
Ui::Text::RichLangValue);
|
||||
} else if (count == 2) {
|
||||
return phrase2(
|
||||
lt_user,
|
||||
nameValue(users.front()),
|
||||
lt_second_user,
|
||||
nameValue(users[1]),
|
||||
Ui::Text::RichLangValue);
|
||||
} else if (count == 3) {
|
||||
return phrase3(
|
||||
lt_user,
|
||||
nameValue(users.front()),
|
||||
lt_second_user,
|
||||
nameValue(users[1]),
|
||||
lt_name,
|
||||
nameValue(users[2]),
|
||||
Ui::Text::RichLangValue);
|
||||
} else {
|
||||
return phraseMore(
|
||||
lt_count,
|
||||
rpl::single(count - kUserpicsMax) | tr::to_count(),
|
||||
lt_user,
|
||||
nameValue(users.front()),
|
||||
lt_second_user,
|
||||
nameValue(users[1]),
|
||||
lt_name,
|
||||
nameValue(users[2]),
|
||||
Ui::Text::RichLangValue);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] not_null<Ui::RpWidget*> CircleBadge(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const QString &text) {
|
||||
const auto widget = Ui::CreateChild<Ui::RpWidget>(parent.get());
|
||||
|
||||
const auto full = Rect(st::premiumGiftsUserpicBadgeSize);
|
||||
const auto inner = full - Margins(st::premiumGiftsUserpicBadgeInner);
|
||||
auto gradient = QLinearGradient(
|
||||
QPointF(0, full.height()),
|
||||
QPointF(full.width(), 0));
|
||||
gradient.setStops(Ui::Premium::GiftGradientStops());
|
||||
|
||||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = QPainter(widget);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::boxBg);
|
||||
p.drawEllipse(full);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(gradient);
|
||||
p.drawEllipse(inner);
|
||||
p.setFont(st::premiumGiftsUserpicBadgeFont);
|
||||
p.setPen(st::premiumButtonFg);
|
||||
p.drawText(full, text, style::al_center);
|
||||
}, widget->lifetime());
|
||||
widget->resize(full.size());
|
||||
return widget;
|
||||
}
|
||||
|
||||
[[nodiscard]] not_null<Ui::RpWidget*> UserpicsContainer(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
std::vector<not_null<UserData*>> users) {
|
||||
Expects(!users.empty());
|
||||
|
||||
if (users.size() == 1) {
|
||||
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
||||
parent.get(),
|
||||
users.front(),
|
||||
st::defaultUserpicButton);
|
||||
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
return userpic;
|
||||
}
|
||||
|
||||
const auto &singleSize = st::defaultUserpicButton.size;
|
||||
|
||||
const auto container = Ui::CreateChild<Ui::RpWidget>(parent.get());
|
||||
const auto single = singleSize.width();
|
||||
const auto shift = single - st::boostReplaceUserpicsShift;
|
||||
const auto maxWidth = users.size() * (single - shift) + shift;
|
||||
container->resize(maxWidth, singleSize.height());
|
||||
container->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
const auto diff = (single - st::premiumGiftsUserpicButton.size.width())
|
||||
/ 2;
|
||||
for (auto i = 0; i < users.size(); i++) {
|
||||
const auto bg = Ui::CreateChild<Ui::RpWidget>(container);
|
||||
bg->resize(singleSize);
|
||||
bg->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = QPainter(bg);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::boxBg);
|
||||
p.drawEllipse(bg->rect());
|
||||
}, bg->lifetime());
|
||||
bg->moveToLeft(std::max(0, i * (single - shift)), 0);
|
||||
|
||||
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
||||
bg,
|
||||
users[i],
|
||||
st::premiumGiftsUserpicButton);
|
||||
userpic->moveToLeft(diff, diff);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
void GiftBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
@@ -95,12 +237,11 @@ void GiftBox(
|
||||
+ st::defaultUserpicButton.size.height()));
|
||||
|
||||
using ColoredMiniStars = Ui::Premium::ColoredMiniStars;
|
||||
const auto stars = box->lifetime().make_state<ColoredMiniStars>(top, true);
|
||||
|
||||
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
||||
const auto stars = box->lifetime().make_state<ColoredMiniStars>(
|
||||
top,
|
||||
user,
|
||||
st::defaultUserpicButton);
|
||||
true);
|
||||
|
||||
const auto userpic = UserpicsContainer(top, { user });
|
||||
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
top->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
@@ -211,7 +352,7 @@ void GiftBox(
|
||||
auto raw = Settings::CreateSubscribeButton({
|
||||
controller,
|
||||
box,
|
||||
[] { return QString("gift"); },
|
||||
[] { return u"gift"_q; },
|
||||
state->buttonText.events(),
|
||||
Ui::Premium::GiftGradientStops(),
|
||||
[=] {
|
||||
@@ -222,10 +363,8 @@ void GiftBox(
|
||||
},
|
||||
});
|
||||
auto button = object_ptr<Ui::GradientButton>::fromRaw(raw);
|
||||
button->resizeToWidth(boxWidth
|
||||
- stButton.buttonPadding.left()
|
||||
- stButton.buttonPadding.right());
|
||||
box->setShowFinishedCallback([raw = button.data()]{
|
||||
button->resizeToWidth(boxWidth - rect::m::sum::h(stButton.buttonPadding));
|
||||
box->setShowFinishedCallback([raw = button.data()] {
|
||||
raw->startGlareAnimation();
|
||||
});
|
||||
box->addButton(std::move(button));
|
||||
@@ -239,6 +378,316 @@ void GiftBox(
|
||||
}, box->lifetime());
|
||||
}
|
||||
|
||||
void GiftsBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::vector<not_null<UserData*>> users,
|
||||
not_null<Api::PremiumGiftCodeOptions*> api,
|
||||
const QString &ref) {
|
||||
Expects(!users.empty());
|
||||
|
||||
const auto boxWidth = st::boxWideWidth;
|
||||
box->setWidth(boxWidth);
|
||||
box->setNoContentMargin(true);
|
||||
const auto buttonsParent = box->verticalLayout().get();
|
||||
const auto session = &users.front()->session();
|
||||
|
||||
struct State {
|
||||
rpl::event_stream<QString> buttonText;
|
||||
rpl::variable<bool> confirmButtonBusy = false;
|
||||
rpl::variable<bool> isPaymentComplete = false;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
|
||||
const auto userpicPadding = st::premiumGiftUserpicPadding;
|
||||
const auto top = box->addRow(object_ptr<Ui::FixedHeightWidget>(
|
||||
buttonsParent,
|
||||
userpicPadding.top()
|
||||
+ userpicPadding.bottom()
|
||||
+ st::defaultUserpicButton.size.height()));
|
||||
|
||||
using ColoredMiniStars = Ui::Premium::ColoredMiniStars;
|
||||
const auto stars = box->lifetime().make_state<ColoredMiniStars>(
|
||||
top,
|
||||
true);
|
||||
|
||||
const auto maxWithUserpic = std::min(users.size(), kUserpicsMax);
|
||||
const auto userpics = UserpicsContainer(
|
||||
top,
|
||||
{ users.begin(), users.begin() + maxWithUserpic });
|
||||
top->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
userpics->moveToLeft(
|
||||
(width - userpics->width()) / 2,
|
||||
userpicPadding.top());
|
||||
|
||||
const auto center = top->rect().center();
|
||||
const auto size = QSize(
|
||||
userpics->width() * Ui::Premium::MiniStars::kSizeFactor,
|
||||
userpics->height());
|
||||
const auto ministarsRect = QRect(
|
||||
QPoint(center.x() - size.width(), center.y() - size.height()),
|
||||
QPoint(center.x() + size.width(), center.y() + size.height()));
|
||||
stars->setPosition(ministarsRect.topLeft());
|
||||
stars->setSize(ministarsRect.size());
|
||||
}, userpics->lifetime());
|
||||
if (const auto rest = users.size() - maxWithUserpic; rest > 0) {
|
||||
const auto badge = CircleBadge(
|
||||
userpics,
|
||||
QChar('+') + QString::number(rest));
|
||||
badge->moveToRight(0, userpics->height() - badge->height());
|
||||
}
|
||||
|
||||
top->paintRequest(
|
||||
) | rpl::start_with_next([=](const QRect &r) {
|
||||
auto p = QPainter(top);
|
||||
|
||||
p.fillRect(r, Qt::transparent);
|
||||
stars->paint(p);
|
||||
}, top->lifetime());
|
||||
|
||||
const auto close = Ui::CreateChild<Ui::IconButton>(
|
||||
buttonsParent,
|
||||
st::infoTopBarClose);
|
||||
close->setClickedCallback([=] { box->closeBox(); });
|
||||
|
||||
buttonsParent->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
close->moveToRight(0, 0, width);
|
||||
}, close->lifetime());
|
||||
|
||||
// Header.
|
||||
const auto &padding = st::premiumGiftAboutPadding;
|
||||
const auto available = boxWidth - padding.left() - padding.right();
|
||||
const auto &stTitle = st::premiumPreviewAboutTitle;
|
||||
auto titleLabel = object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
rpl::conditional(
|
||||
state->isPaymentComplete.value(),
|
||||
tr::lng_premium_gifts_about_paid_title(),
|
||||
tr::lng_premium_gift_title()),
|
||||
stTitle);
|
||||
titleLabel->resizeToWidth(available);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
box,
|
||||
std::move(titleLabel)),
|
||||
st::premiumGiftTitlePadding);
|
||||
|
||||
// About.
|
||||
{
|
||||
const auto emoji = Ui::Text::SingleCustomEmoji(
|
||||
session->data().customEmojiManager().registerInternalEmoji(
|
||||
st::premiumGiftsBoostIcon,
|
||||
QMargins(0, st::premiumGiftsUserpicBadgeInner, 0, 0),
|
||||
false));
|
||||
auto text = rpl::conditional(
|
||||
state->isPaymentComplete.value(),
|
||||
ComplexAboutLabel(
|
||||
users,
|
||||
tr::lng_premium_gifts_about_paid1,
|
||||
tr::lng_premium_gifts_about_paid2,
|
||||
tr::lng_premium_gifts_about_paid3,
|
||||
tr::lng_premium_gifts_about_paid_more
|
||||
) | rpl::map([count = users.size()](TextWithEntities text) {
|
||||
text.append('\n');
|
||||
text.append('\n');
|
||||
text.append(tr::lng_premium_gifts_about_paid_below(
|
||||
tr::now,
|
||||
lt_count,
|
||||
float64(count),
|
||||
Ui::Text::RichLangValue));
|
||||
return text;
|
||||
}),
|
||||
ComplexAboutLabel(
|
||||
users,
|
||||
tr::lng_premium_gifts_about_user1,
|
||||
tr::lng_premium_gifts_about_user2,
|
||||
tr::lng_premium_gifts_about_user3,
|
||||
tr::lng_premium_gifts_about_user_more
|
||||
) | rpl::map([=, count = users.size()](TextWithEntities text) {
|
||||
text.append('\n');
|
||||
text.append('\n');
|
||||
text.append(tr::lng_premium_gifts_about_reward(
|
||||
tr::now,
|
||||
lt_count,
|
||||
count * BoostsForGift(session),
|
||||
lt_emoji,
|
||||
emoji,
|
||||
Ui::Text::RichLangValue));
|
||||
return text;
|
||||
})
|
||||
);
|
||||
const auto label = box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(box, st::premiumPreviewAbout)),
|
||||
padding)->entity();
|
||||
std::move(
|
||||
text
|
||||
) | rpl::start_with_next([=](const TextWithEntities &t) {
|
||||
using namespace Core;
|
||||
label->setMarkedText(t, MarkedTextContext{ .session = session });
|
||||
}, label->lifetime());
|
||||
label->setTextColorOverride(stTitle.textFg->c);
|
||||
label->resizeToWidth(available);
|
||||
}
|
||||
|
||||
// List.
|
||||
const auto optionsContainer = buttonsParent->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
buttonsParent,
|
||||
object_ptr<Ui::VerticalLayout>(buttonsParent)));
|
||||
const auto options = api->options(users.size());
|
||||
const auto group = std::make_shared<Ui::RadiobuttonGroup>();
|
||||
const auto groupValueChangedCallback = [=](int value) {
|
||||
Expects(value < options.size() && value >= 0);
|
||||
auto text = tr::lng_premium_gift_button(
|
||||
tr::now,
|
||||
lt_cost,
|
||||
options[value].costTotal);
|
||||
state->buttonText.fire(std::move(text));
|
||||
};
|
||||
group->setChangedCallback(groupValueChangedCallback);
|
||||
Ui::Premium::AddGiftOptions(
|
||||
optionsContainer->entity(),
|
||||
group,
|
||||
options,
|
||||
st::premiumGiftOption);
|
||||
optionsContainer->toggleOn(
|
||||
state->isPaymentComplete.value() | rpl::map(!rpl::mappers::_1),
|
||||
anim::type::instant);
|
||||
|
||||
// Summary.
|
||||
{
|
||||
{
|
||||
// Will be hidden after payment.
|
||||
const auto content = optionsContainer->entity();
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddDivider(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSubsectionTitle(
|
||||
content,
|
||||
tr::lng_premium_gifts_summary_subtitle());
|
||||
}
|
||||
const auto content = box->addRow(
|
||||
object_ptr<Ui::VerticalLayout>(box),
|
||||
{});
|
||||
auto buttonCallback = [=](PremiumPreview section) {
|
||||
stars->setPaused(true);
|
||||
const auto previewBoxShown = [=](
|
||||
not_null<Ui::BoxContent*> previewBox) {
|
||||
previewBox->boxClosing(
|
||||
) | rpl::start_with_next(crl::guard(box, [=] {
|
||||
stars->setPaused(false);
|
||||
}), previewBox->lifetime());
|
||||
};
|
||||
|
||||
ShowPremiumPreviewBox(
|
||||
controller->uiShow(),
|
||||
section,
|
||||
previewBoxShown,
|
||||
true);
|
||||
};
|
||||
Settings::AddSummaryPremium(
|
||||
content,
|
||||
controller,
|
||||
ref,
|
||||
std::move(buttonCallback));
|
||||
}
|
||||
|
||||
// Footer.
|
||||
{
|
||||
box->addRow(
|
||||
object_ptr<Ui::DividerLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_premium_gifts_terms(
|
||||
lt_link,
|
||||
tr::lng_payments_terms_link(
|
||||
) | rpl::map([](const QString &t) {
|
||||
using namespace Ui::Text;
|
||||
return Link(t, u"https://telegram.org/tos"_q);
|
||||
}),
|
||||
lt_policy,
|
||||
tr::lng_premium_gifts_terms_policy(
|
||||
) | rpl::map([](const QString &t) {
|
||||
using namespace Ui::Text;
|
||||
return Link(t, u"https://telegram.org/privacy"_q);
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
st::premiumGiftTerms),
|
||||
st::defaultBoxDividerLabelPadding),
|
||||
{});
|
||||
}
|
||||
|
||||
// Button.
|
||||
const auto &stButton = st::premiumGiftBox;
|
||||
box->setStyle(stButton);
|
||||
auto raw = Settings::CreateSubscribeButton({
|
||||
controller,
|
||||
box,
|
||||
[=] { return ref; },
|
||||
rpl::combine(
|
||||
state->buttonText.events(),
|
||||
state->confirmButtonBusy.value(),
|
||||
state->isPaymentComplete.value()
|
||||
) | rpl::map([](const QString &text, bool busy, bool paid) {
|
||||
return busy
|
||||
? QString()
|
||||
: paid
|
||||
? tr::lng_close(tr::now)
|
||||
: text;
|
||||
}),
|
||||
Ui::Premium::GiftGradientStops(),
|
||||
});
|
||||
raw->setClickedCallback([=] {
|
||||
if (state->confirmButtonBusy.current()) {
|
||||
return;
|
||||
}
|
||||
if (state->isPaymentComplete.current()) {
|
||||
return box->closeBox();
|
||||
}
|
||||
auto invoice = api->invoice(
|
||||
users.size(),
|
||||
api->monthsFromPreset(group->value()));
|
||||
invoice.purpose = Payments::InvoicePremiumGiftCodeUsers{ users };
|
||||
|
||||
state->confirmButtonBusy = true;
|
||||
const auto show = box->uiShow();
|
||||
const auto weak = Ui::MakeWeak(box.get());
|
||||
const auto done = [=](Payments::CheckoutResult result) {
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->window()->setFocus();
|
||||
state->confirmButtonBusy = false;
|
||||
if (result == Payments::CheckoutResult::Paid) {
|
||||
state->isPaymentComplete = true;
|
||||
Ui::StartFireworks(box->parentWidget());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Payments::CheckoutProcess::Start(std::move(invoice), done);
|
||||
});
|
||||
{
|
||||
using namespace Info::Statistics;
|
||||
const auto loadingAnimation = InfiniteRadialAnimationWidget(
|
||||
raw,
|
||||
raw->height() / 2);
|
||||
AddChildToWidgetCenter(raw, loadingAnimation);
|
||||
loadingAnimation->showOn(state->confirmButtonBusy.value());
|
||||
}
|
||||
auto button = object_ptr<Ui::GradientButton>::fromRaw(raw);
|
||||
button->resizeToWidth(boxWidth - rect::m::sum::h(stButton.buttonPadding));
|
||||
box->setShowFinishedCallback([raw = button.data()] {
|
||||
raw->startGlareAnimation();
|
||||
});
|
||||
box->addButton(std::move(button));
|
||||
|
||||
groupValueChangedCallback(0);
|
||||
}
|
||||
|
||||
[[nodiscard]] Data::GiftCodeLink MakeGiftCodeLink(
|
||||
not_null<Main::Session*> session,
|
||||
const QString &slug) {
|
||||
@@ -370,18 +819,20 @@ void AddTable(
|
||||
container,
|
||||
st::giveawayGiftCodeTable),
|
||||
st::giveawayGiftCodeTableMargin);
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_link_label_from(),
|
||||
controller,
|
||||
current.from);
|
||||
if (current.to) {
|
||||
if (current.from) {
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_link_label_from(),
|
||||
controller,
|
||||
current.from);
|
||||
}
|
||||
if (current.from && current.to) {
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_link_label_to(),
|
||||
controller,
|
||||
current.to);
|
||||
} else {
|
||||
} else if (current.from) {
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_link_label_to(),
|
||||
@@ -394,7 +845,7 @@ void AddTable(
|
||||
lt_duration,
|
||||
GiftDurationValue(current.months) | Ui::Text::ToWithEntities(),
|
||||
Ui::Text::WithEntities));
|
||||
if (!skipReason) {
|
||||
if (!skipReason && current.from) {
|
||||
const auto reason = AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_link_label_reason(),
|
||||
@@ -439,6 +890,116 @@ void GiftPremiumValidator::cancel() {
|
||||
_requestId = 0;
|
||||
}
|
||||
|
||||
void GiftPremiumValidator::showChoosePeerBox(const QString &ref) {
|
||||
if (_manyGiftsLifetime) {
|
||||
return;
|
||||
}
|
||||
using namespace Api;
|
||||
const auto api = _manyGiftsLifetime.make_state<PremiumGiftCodeOptions>(
|
||||
_controller->session().user());
|
||||
const auto show = _controller->uiShow();
|
||||
api->request(
|
||||
) | rpl::start_with_error_done([=](const QString &error) {
|
||||
show->showToast(error);
|
||||
}, [=] {
|
||||
const auto maxAmount = *ranges::max_element(api->availablePresets());
|
||||
|
||||
class Controller final : public ContactsBoxController {
|
||||
public:
|
||||
Controller(
|
||||
not_null<Main::Session*> session,
|
||||
Fn<bool(int)> checkErrorCallback)
|
||||
: ContactsBoxController(session)
|
||||
, _checkErrorCallback(std::move(checkErrorCallback)) {
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<PeerListRow> createRow(
|
||||
not_null<UserData*> user) override {
|
||||
if (user->isSelf()
|
||||
|| user->isBot()
|
||||
|| user->isServiceUser()
|
||||
|| user->isInaccessible()) {
|
||||
return nullptr;
|
||||
}
|
||||
return ContactsBoxController::createRow(user);
|
||||
}
|
||||
|
||||
void rowClicked(not_null<PeerListRow*> row) override {
|
||||
const auto checked = !row->checked();
|
||||
if (checked
|
||||
&& _checkErrorCallback
|
||||
&& _checkErrorCallback(
|
||||
delegate()->peerListSelectedRowsCount())) {
|
||||
return;
|
||||
}
|
||||
delegate()->peerListSetRowChecked(row, checked);
|
||||
}
|
||||
|
||||
private:
|
||||
const Fn<bool(int)> _checkErrorCallback;
|
||||
|
||||
};
|
||||
auto initBox = [=](not_null<PeerListBox*> peersBox) {
|
||||
const auto ignoreClose = peersBox->lifetime().make_state<bool>(0);
|
||||
|
||||
auto process = [=] {
|
||||
const auto selected = peersBox->collectSelectedRows();
|
||||
const auto users = ranges::views::all(
|
||||
selected
|
||||
) | ranges::views::transform([](not_null<PeerData*> p) {
|
||||
return p->asUser();
|
||||
}) | ranges::views::filter([](UserData *u) -> bool {
|
||||
return u;
|
||||
}) | ranges::to<std::vector<not_null<UserData*>>>();
|
||||
if (!users.empty()) {
|
||||
const auto giftBox = show->show(
|
||||
Box(GiftsBox, _controller, users, api, ref));
|
||||
giftBox->boxClosing(
|
||||
) | rpl::start_with_next([=] {
|
||||
_manyGiftsLifetime.destroy();
|
||||
}, giftBox->lifetime());
|
||||
}
|
||||
(*ignoreClose) = true;
|
||||
peersBox->closeBox();
|
||||
};
|
||||
|
||||
peersBox->setTitle(tr::lng_premium_gift_title());
|
||||
peersBox->addButton(
|
||||
tr::lng_settings_gift_premium_users_confirm(),
|
||||
std::move(process));
|
||||
peersBox->addButton(tr::lng_cancel(), [=] {
|
||||
peersBox->closeBox();
|
||||
});
|
||||
peersBox->boxClosing(
|
||||
) | rpl::start_with_next([=] {
|
||||
if (!(*ignoreClose)) {
|
||||
_manyGiftsLifetime.destroy();
|
||||
}
|
||||
}, peersBox->lifetime());
|
||||
};
|
||||
|
||||
auto listController = std::make_unique<Controller>(
|
||||
&_controller->session(),
|
||||
[=](int count) {
|
||||
if (count <= maxAmount) {
|
||||
return false;
|
||||
}
|
||||
show->showToast(tr::lng_settings_gift_premium_users_error(
|
||||
tr::now,
|
||||
lt_count,
|
||||
maxAmount));
|
||||
return true;
|
||||
});
|
||||
show->showBox(
|
||||
Box<PeerListBox>(
|
||||
std::move(listController),
|
||||
std::move(initBox)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
|
||||
}, _manyGiftsLifetime);
|
||||
}
|
||||
|
||||
void GiftPremiumValidator::showBox(not_null<UserData*> user) {
|
||||
if (_requestId) {
|
||||
return;
|
||||
@@ -493,7 +1054,7 @@ void GiftCodeBox(
|
||||
state->data = session->api().premium().giftCodeValue(slug);
|
||||
state->used = state->data.value(
|
||||
) | rpl::map([=](const Api::GiftCode &data) {
|
||||
return data.used;
|
||||
return data.used != 0;
|
||||
});
|
||||
|
||||
box->setWidth(st::boxWideWidth);
|
||||
@@ -723,10 +1284,22 @@ void GiftCodePendingBox(
|
||||
|
||||
void ResolveGiftCode(
|
||||
not_null<Window::SessionNavigation*> controller,
|
||||
const QString &slug) {
|
||||
const QString &slug,
|
||||
PeerId fromId,
|
||||
PeerId toId) {
|
||||
const auto done = [=](Api::GiftCode code) {
|
||||
const auto session = &controller->session();
|
||||
const auto selfId = session->userPeerId();
|
||||
if (!code) {
|
||||
controller->showToast(tr::lng_gift_link_expired(tr::now));
|
||||
} else if (!code.from && fromId == selfId) {
|
||||
code.from = fromId;
|
||||
code.to = toId;
|
||||
const auto self = (fromId == selfId);
|
||||
const auto peer = session->data().peer(self ? toId : fromId);
|
||||
const auto months = code.months;
|
||||
const auto parent = controller->parentController();
|
||||
Settings::ShowGiftPremium(parent, peer, months, self);
|
||||
} else {
|
||||
controller->uiShow()->showBox(Box(GiftCodeBox, controller, slug));
|
||||
}
|
||||
@@ -739,8 +1312,11 @@ void ResolveGiftCode(
|
||||
void GiveawayInfoBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionNavigation*> controller,
|
||||
Data::Giveaway giveaway,
|
||||
std::optional<Data::GiveawayStart> start,
|
||||
std::optional<Data::GiveawayResults> results,
|
||||
Api::GiveawayInfo info) {
|
||||
Expects(start || results);
|
||||
|
||||
using State = Api::GiveawayState;
|
||||
const auto finished = (info.state == State::Finished)
|
||||
|| (info.state == State::Refunded);
|
||||
@@ -749,10 +1325,31 @@ void GiveawayInfoBox(
|
||||
? tr::lng_prizes_end_title
|
||||
: tr::lng_prizes_how_title)());
|
||||
|
||||
const auto first = !giveaway.channels.empty()
|
||||
? giveaway.channels.front()->name()
|
||||
const auto first = results
|
||||
? results->channel->name()
|
||||
: !start->channels.empty()
|
||||
? start->channels.front()->name()
|
||||
: u"channel"_q;
|
||||
auto text = (finished
|
||||
auto text = TextWithEntities();
|
||||
|
||||
if (!info.giftCode.isEmpty()) {
|
||||
text.append("\n\n");
|
||||
text.append(Ui::Text::Bold(tr::lng_prizes_you_won(
|
||||
tr::now,
|
||||
lt_cup,
|
||||
QString::fromUtf8("\xf0\x9f\x8f\x86"))));
|
||||
text.append("\n\n");
|
||||
} else if (info.state == State::Finished) {
|
||||
text.append("\n\n");
|
||||
text.append(Ui::Text::Bold(tr::lng_prizes_you_didnt(tr::now)));
|
||||
text.append("\n\n");
|
||||
}
|
||||
|
||||
const auto quantity = start
|
||||
? start->quantity
|
||||
: (results->winnersCount + results->unclaimedCount);
|
||||
const auto months = start ? start->months : results->months;
|
||||
text.append((finished
|
||||
? tr::lng_prizes_end_text
|
||||
: tr::lng_prizes_how_text)(
|
||||
tr::now,
|
||||
@@ -760,18 +1357,21 @@ void GiveawayInfoBox(
|
||||
tr::lng_prizes_admins(
|
||||
tr::now,
|
||||
lt_count,
|
||||
giveaway.quantity,
|
||||
quantity,
|
||||
lt_channel,
|
||||
Ui::Text::Bold(first),
|
||||
lt_duration,
|
||||
TextWithEntities{ GiftDuration(giveaway.months) },
|
||||
TextWithEntities{ GiftDuration(months) },
|
||||
Ui::Text::RichLangValue),
|
||||
Ui::Text::RichLangValue);
|
||||
const auto many = (giveaway.channels.size() > 1);
|
||||
Ui::Text::RichLangValue));
|
||||
const auto many = start
|
||||
? (start->channels.size() > 1)
|
||||
: (results->additionalPeersCount > 0);
|
||||
const auto count = info.winnersCount
|
||||
? info.winnersCount
|
||||
: giveaway.quantity;
|
||||
auto winners = giveaway.all
|
||||
: quantity;
|
||||
const auto all = start ? start->all : results->all;
|
||||
auto winners = all
|
||||
? (many
|
||||
? tr::lng_prizes_winners_all_of_many
|
||||
: tr::lng_prizes_winners_all_of_one)(
|
||||
@@ -793,13 +1393,30 @@ void GiveawayInfoBox(
|
||||
Ui::Text::Bold(
|
||||
langDateTime(base::unixtime::parse(info.startDate))),
|
||||
Ui::Text::RichLangValue);
|
||||
const auto additionalPrize = results
|
||||
? results->additionalPrize
|
||||
: start->additionalPrize;
|
||||
if (!additionalPrize.isEmpty()) {
|
||||
text.append("\n\n").append(tr::lng_prizes_additional_added(
|
||||
tr::now,
|
||||
lt_count,
|
||||
count,
|
||||
lt_channel,
|
||||
Ui::Text::Bold(first),
|
||||
lt_prize,
|
||||
TextWithEntities{ additionalPrize },
|
||||
Ui::Text::RichLangValue));
|
||||
}
|
||||
const auto untilDate = start
|
||||
? start->untilDate
|
||||
: results->untilDate;
|
||||
text.append("\n\n").append((finished
|
||||
? tr::lng_prizes_end_when_finish
|
||||
: tr::lng_prizes_how_when_finish)(
|
||||
tr::now,
|
||||
lt_date,
|
||||
Ui::Text::Bold(langDayOfMonthFull(
|
||||
base::unixtime::parse(giveaway.untilDate).date())),
|
||||
base::unixtime::parse(untilDate).date())),
|
||||
lt_winners,
|
||||
winners,
|
||||
Ui::Text::RichLangValue));
|
||||
@@ -810,17 +1427,9 @@ void GiveawayInfoBox(
|
||||
info.activatedCount,
|
||||
Ui::Text::RichLangValue));
|
||||
}
|
||||
if (!info.giftCode.isEmpty()) {
|
||||
text.append("\n\n");
|
||||
text.append(tr::lng_prizes_you_won(
|
||||
tr::now,
|
||||
lt_cup,
|
||||
QString::fromUtf8("\xf0\x9f\x8f\x86")));
|
||||
} else if (info.state == State::Finished) {
|
||||
text.append("\n\n");
|
||||
text.append(tr::lng_prizes_you_didnt(tr::now));
|
||||
} else if (info.state == State::Preparing) {
|
||||
|
||||
if (!info.giftCode.isEmpty()
|
||||
|| info.state == State::Finished
|
||||
|| info.state == State::Preparing) {
|
||||
} else if (info.state != State::Refunded) {
|
||||
if (info.adminChannelId) {
|
||||
const auto channel = controller->session().data().channel(
|
||||
@@ -858,7 +1467,7 @@ void GiveawayInfoBox(
|
||||
Ui::Text::Bold(first),
|
||||
lt_date,
|
||||
Ui::Text::Bold(langDayOfMonthFull(
|
||||
base::unixtime::parse(giveaway.untilDate).date())),
|
||||
base::unixtime::parse(untilDate).date())),
|
||||
Ui::Text::RichLangValue));
|
||||
}
|
||||
}
|
||||
@@ -902,14 +1511,15 @@ void ResolveGiveawayInfo(
|
||||
not_null<Window::SessionNavigation*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
MsgId messageId,
|
||||
Data::Giveaway giveaway) {
|
||||
std::optional<Data::GiveawayStart> start,
|
||||
std::optional<Data::GiveawayResults> results) {
|
||||
const auto show = [=](Api::GiveawayInfo info) {
|
||||
if (!info) {
|
||||
controller->showToast(
|
||||
tr::lng_confirm_phone_link_invalid(tr::now));
|
||||
} else {
|
||||
controller->uiShow()->showBox(
|
||||
Box(GiveawayInfoBox, controller, giveaway, info));
|
||||
Box(GiveawayInfoBox, controller, start, results, info));
|
||||
}
|
||||
};
|
||||
controller->session().api().premium().resolveGiveawayInfo(
|
||||
|
||||
@@ -16,7 +16,8 @@ struct GiftCode;
|
||||
} // namespace Api
|
||||
|
||||
namespace Data {
|
||||
struct Giveaway;
|
||||
struct GiveawayStart;
|
||||
struct GiveawayResults;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
@@ -33,6 +34,7 @@ public:
|
||||
GiftPremiumValidator(not_null<Window::SessionController*> controller);
|
||||
|
||||
void showBox(not_null<UserData*> user);
|
||||
void showChoosePeerBox(const QString &ref);
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
@@ -41,6 +43,8 @@ private:
|
||||
|
||||
mtpRequestId _requestId = 0;
|
||||
|
||||
rpl::lifetime _manyGiftsLifetime;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] rpl::producer<QString> GiftDurationValue(int months);
|
||||
@@ -56,10 +60,13 @@ void GiftCodePendingBox(
|
||||
const Api::GiftCode &data);
|
||||
void ResolveGiftCode(
|
||||
not_null<Window::SessionNavigation*> controller,
|
||||
const QString &slug);
|
||||
const QString &slug,
|
||||
PeerId fromId = 0,
|
||||
PeerId toId = 0);
|
||||
|
||||
void ResolveGiveawayInfo(
|
||||
not_null<Window::SessionNavigation*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
MsgId messageId,
|
||||
Data::Giveaway giveaway);
|
||||
std::optional<Data::GiveawayStart> start,
|
||||
std::optional<Data::GiveawayResults> results);
|
||||
|
||||