Compare commits
356 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7958f5235 | ||
|
|
2ae5fc0fac | ||
|
|
666251f23e | ||
|
|
d89d8b09da | ||
|
|
0aa20b4479 | ||
|
|
8658dba97a | ||
|
|
20c911651f | ||
|
|
bef20ba4a2 | ||
|
|
ee325031a0 | ||
|
|
b57549546d | ||
|
|
1e4d278604 | ||
|
|
80aa596310 | ||
|
|
4913288061 | ||
|
|
1a43cd8a67 | ||
|
|
bcb0511083 | ||
|
|
2dda044dd1 | ||
|
|
da18ab3d41 | ||
|
|
f26cae8807 | ||
|
|
641bb01ba2 | ||
|
|
02c9b61840 | ||
|
|
99e8d22c51 | ||
|
|
cd9b3368da | ||
|
|
f918c6bb83 | ||
|
|
28dff5ba6d | ||
|
|
4d978f5b36 | ||
|
|
ef41878815 | ||
|
|
6a663932f3 | ||
|
|
67c538ae8f | ||
|
|
38137e16a0 | ||
|
|
9c01295521 | ||
|
|
c7b6db00ca | ||
|
|
7f0bdc5d36 | ||
|
|
5f6d8f74dd | ||
|
|
9086319b99 | ||
|
|
37cd4f51eb | ||
|
|
74416568d6 | ||
|
|
a8c3d6c39b | ||
|
|
5939c2dbfc | ||
|
|
4bef1e9f59 | ||
|
|
18bf48bf90 | ||
|
|
b415b293cf | ||
|
|
3a1bb1966d | ||
|
|
d4b686ff65 | ||
|
|
8148974e9f | ||
|
|
eaa2573c66 | ||
|
|
4d315f8e61 | ||
|
|
e5981ed22b | ||
|
|
eccaca8808 | ||
|
|
36e6c76b59 | ||
|
|
06c35f5b51 | ||
|
|
ca21b7efae | ||
|
|
9eb20ede33 | ||
|
|
656146c445 | ||
|
|
2eb8ed59cc | ||
|
|
9d6eeace54 | ||
|
|
315c85fb8d | ||
|
|
b7a70a2f28 | ||
|
|
63bf564757 | ||
|
|
6a9c5818ba | ||
|
|
98e7de01b0 | ||
|
|
8ef7325e16 | ||
|
|
17de379145 | ||
|
|
eb784c665a | ||
|
|
07beb3e86b | ||
|
|
18919a6b4a | ||
|
|
71d4b64691 | ||
|
|
9918a20946 | ||
|
|
ae5e7d641a | ||
|
|
4e88ea970e | ||
|
|
f6ff0f3b2c | ||
|
|
84d58e574f | ||
|
|
1dd7cc956b | ||
|
|
54e7dfe986 | ||
|
|
4e9a52343f | ||
|
|
51c805d77a | ||
|
|
8bde488662 | ||
|
|
4f1e04cf9e | ||
|
|
e509da8fd8 | ||
|
|
f6bfe2c9a8 | ||
|
|
15719b73b4 | ||
|
|
8da9638563 | ||
|
|
a5afeebc0c | ||
|
|
9903266722 | ||
|
|
8f33d5903d | ||
|
|
e9c79886d2 | ||
|
|
6207770120 | ||
|
|
2a99f1a1ef | ||
|
|
4aafcebef5 | ||
|
|
3a78e94f2f | ||
|
|
b5aca56914 | ||
|
|
a6621233d0 | ||
|
|
b4eb25de58 | ||
|
|
d96a8d028a | ||
|
|
f45c47f3d5 | ||
|
|
827ce46d3c | ||
|
|
91c84d63de | ||
|
|
545392f90f | ||
|
|
fa61cf3c85 | ||
|
|
c359646702 | ||
|
|
51fef843f0 | ||
|
|
1a3a0fb124 | ||
|
|
f1d9cca119 | ||
|
|
8e749173de | ||
|
|
20dbf18106 | ||
|
|
10ff71e8f6 | ||
|
|
170cc77a1b | ||
|
|
589673e420 | ||
|
|
2f9c39fe53 | ||
|
|
044c7f3ce9 | ||
|
|
d18e28978a | ||
|
|
581b84afe0 | ||
|
|
846cabeda5 | ||
|
|
9b59ef00af | ||
|
|
079772a399 | ||
|
|
2e39befd7c | ||
|
|
219ffd2c48 | ||
|
|
63d15e4479 | ||
|
|
2f01efdd64 | ||
|
|
8b7d2c880e | ||
|
|
1755ead681 | ||
|
|
1e1f7be708 | ||
|
|
7170bec25d | ||
|
|
7ad1a7dd37 | ||
|
|
217e9b2475 | ||
|
|
28f2c213f7 | ||
|
|
b8f1cebeb6 | ||
|
|
6a3ad52aef | ||
|
|
8c349c0515 | ||
|
|
9038dfb3b8 | ||
|
|
2e94488eb4 | ||
|
|
59ed41abfe | ||
|
|
8bea6776f5 | ||
|
|
0143d22a21 | ||
|
|
021d0053be | ||
|
|
443ca0b390 | ||
|
|
2e0224589f | ||
|
|
f8da59595a | ||
|
|
e8568c6701 | ||
|
|
4ca3f6a1b3 | ||
|
|
6af255923a | ||
|
|
624d83dc60 | ||
|
|
6073da2843 | ||
|
|
ca5d2c115d | ||
|
|
86f3d88116 | ||
|
|
7a971b5855 | ||
|
|
0a7de3340a | ||
|
|
2cb73eefeb | ||
|
|
152aa06930 | ||
|
|
cbca78ff63 | ||
|
|
7c46b292ac | ||
|
|
883509903f | ||
|
|
37b2951058 | ||
|
|
ceb323ac7c | ||
|
|
b65d40a22b | ||
|
|
aed49b9289 | ||
|
|
72d81cc52f | ||
|
|
4bb3aec168 | ||
|
|
c0a81f2428 | ||
|
|
692adacc2a | ||
|
|
26dbeb6831 | ||
|
|
07f72c20eb | ||
|
|
8407b0cccf | ||
|
|
3ff17a8789 | ||
|
|
a9a6d8a568 | ||
|
|
9877845b9c | ||
|
|
dc89262461 | ||
|
|
a5425042cf | ||
|
|
d6e03c3e48 | ||
|
|
e91eecf34f | ||
|
|
1a8cc87e60 | ||
|
|
0863941642 | ||
|
|
b331aee599 | ||
|
|
e19180cc86 | ||
|
|
10cb891f48 | ||
|
|
c8f7a8c795 | ||
|
|
74a28ffdf7 | ||
|
|
ecedce0c2f | ||
|
|
bd4f993292 | ||
|
|
4934b026d3 | ||
|
|
11f183a79f | ||
|
|
ae426a41e0 | ||
|
|
d6edc3728d | ||
|
|
e121487170 | ||
|
|
72a093ec77 | ||
|
|
4996d90782 | ||
|
|
9a451a1423 | ||
|
|
4d11ad45db | ||
|
|
1657c2c7f2 | ||
|
|
c5e7048a3d | ||
|
|
1f194da2f0 | ||
|
|
0954b04f24 | ||
|
|
4659499340 | ||
|
|
6eb4584408 | ||
|
|
4ba4b77b95 | ||
|
|
f9bf6dbc1e | ||
|
|
64b5269648 | ||
|
|
f394cecf55 | ||
|
|
8b56676c23 | ||
|
|
e2713ea627 | ||
|
|
f5e50409d3 | ||
|
|
050916a56a | ||
|
|
cdf36cc387 | ||
|
|
f909a36cbd | ||
|
|
1d36255ca5 | ||
|
|
acfdae2d72 | ||
|
|
ab59e97b92 | ||
|
|
1060b04b1e | ||
|
|
df044dbd83 | ||
|
|
5eb210ec12 | ||
|
|
f24f78c0cc | ||
|
|
8a071fe1fe | ||
|
|
f3e84de5fb | ||
|
|
2dec1b72f7 | ||
|
|
604a827a52 | ||
|
|
3d8b303ab7 | ||
|
|
2c599e60c3 | ||
|
|
928d8feb21 | ||
|
|
490e688a91 | ||
|
|
34c36d77c3 | ||
|
|
0ab26f0c82 | ||
|
|
db453ab7ae | ||
|
|
e032dbf383 | ||
|
|
3b4ed03105 | ||
|
|
963694330d | ||
|
|
2733b12cff | ||
|
|
a377364621 | ||
|
|
58f4884deb | ||
|
|
c2c7a25487 | ||
|
|
f98c08f4c6 | ||
|
|
cfc2a959cf | ||
|
|
6a1630a84c | ||
|
|
a51be85199 | ||
|
|
e0fd5d8795 | ||
|
|
8659f60b46 | ||
|
|
c43699fb43 | ||
|
|
c56a22c8d5 | ||
|
|
7f27ce6dee | ||
|
|
508ba4750c | ||
|
|
c0b19000d6 | ||
|
|
409a3357da | ||
|
|
82523978c9 | ||
|
|
718ba2d0e3 | ||
|
|
2317dd8820 | ||
|
|
df06f55c7f | ||
|
|
d43853460e | ||
|
|
28fee318d7 | ||
|
|
1ec2ecac11 | ||
|
|
7aa3956792 | ||
|
|
d349759618 | ||
|
|
ac3e4fb42f | ||
|
|
eccb01e5b5 | ||
|
|
7c8d10022f | ||
|
|
f1244e19a1 | ||
|
|
e17143dd8b | ||
|
|
39d5d3a1cf | ||
|
|
f8be5731a5 | ||
|
|
ab248febcd | ||
|
|
d4afba3a24 | ||
|
|
4ee9751feb | ||
|
|
46fb5ee1d2 | ||
|
|
749f837df5 | ||
|
|
e11904e05b | ||
|
|
e1aa08b985 | ||
|
|
2af3770b29 | ||
|
|
74f9d0935b | ||
|
|
f9c50fdc06 | ||
|
|
1fa825321d | ||
|
|
d9147562e5 | ||
|
|
5b569718ec | ||
|
|
a5d4746202 | ||
|
|
50d150302d | ||
|
|
e451eb5126 | ||
|
|
a626364430 | ||
|
|
b55ed7214a | ||
|
|
d6801517bb | ||
|
|
97dde7eb56 | ||
|
|
10df3dce7c | ||
|
|
889d7c0c15 | ||
|
|
799155279f | ||
|
|
10e7bd0d6e | ||
|
|
0a99487091 | ||
|
|
4965c19314 | ||
|
|
30810e95f4 | ||
|
|
a3d84f69ea | ||
|
|
b3bb1a537c | ||
|
|
726aa3316d | ||
|
|
ba6c3eaf73 | ||
|
|
ebe45f3fa1 | ||
|
|
3f0fed19d8 | ||
|
|
609cab6e2f | ||
|
|
7b3cb0c3dd | ||
|
|
43559fb6b7 | ||
|
|
8788692fb3 | ||
|
|
072e346324 | ||
|
|
99f65ab5ec | ||
|
|
fe7b120003 | ||
|
|
cb8f86bc8d | ||
|
|
18e6e2da9e | ||
|
|
54247cd11b | ||
|
|
8b0725650d | ||
|
|
20411be9bd | ||
|
|
f4f36d85b9 | ||
|
|
9f887237eb | ||
|
|
4e3f917a2c | ||
|
|
5c9c836857 | ||
|
|
31b7fe6ba0 | ||
|
|
102c0a96ed | ||
|
|
9a0be43ef5 | ||
|
|
c1d948ef63 | ||
|
|
9df229a230 | ||
|
|
a1c342c822 | ||
|
|
c313cfb4ec | ||
|
|
8d4a658d0b | ||
|
|
86f53d3eff | ||
|
|
3cb89339c8 | ||
|
|
ba98a8df32 | ||
|
|
f3faa52bc7 | ||
|
|
3dac08d34f | ||
|
|
deba090cbd | ||
|
|
5b01f9530b | ||
|
|
df66162bca | ||
|
|
d413080f83 | ||
|
|
38ee57f852 | ||
|
|
c632316ad7 | ||
|
|
bba7010e74 | ||
|
|
edf93b0031 | ||
|
|
611be90880 | ||
|
|
68886e1b61 | ||
|
|
67319c1612 | ||
|
|
da8db0157f | ||
|
|
6188268afd | ||
|
|
cd0db53bac | ||
|
|
5bb90679a8 | ||
|
|
72df3a8f91 | ||
|
|
5fe2e649fb | ||
|
|
9eba8ccc73 | ||
|
|
bb3c91aa44 | ||
|
|
9f1268b6c8 | ||
|
|
a6f1a1bd62 | ||
|
|
1b2642b017 | ||
|
|
e722645e7c | ||
|
|
9486c266b5 | ||
|
|
dc21491099 | ||
|
|
2f48bbd317 | ||
|
|
450f9ca91e | ||
|
|
f86e2d98cc | ||
|
|
0cf85be86b | ||
|
|
a15ef8bbc2 | ||
|
|
ecca60afe4 | ||
|
|
1ab0f840f3 | ||
|
|
3623fb1f9a | ||
|
|
4cbfcc8dbc | ||
|
|
90821428d3 | ||
|
|
c5468a1111 | ||
|
|
505ef04134 | ||
|
|
f70c2adbdd |
5
.github/workflows/mac.yml
vendored
@@ -91,6 +91,11 @@ jobs:
|
||||
run: |
|
||||
./$REPO_NAME/Telegram/build/prepare/mac.sh skip-release silent
|
||||
|
||||
- name: Free up some disk space.
|
||||
run: |
|
||||
cd Libraries
|
||||
find . -iname "*.dir" -exec rm -rf {} || true \;
|
||||
|
||||
- name: Telegram Desktop build.
|
||||
if: env.ONLY_CACHE == 'false'
|
||||
run: |
|
||||
|
||||
407
.github/workflows/win.yml
vendored
@@ -44,385 +44,84 @@ jobs:
|
||||
|
||||
windows:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2022
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
defines:
|
||||
- ""
|
||||
arch: [Win32, x64]
|
||||
|
||||
env:
|
||||
SDK: "10.0.18362.0"
|
||||
GIT: "https://github.com"
|
||||
QT_VER: "5.15.2"
|
||||
OPENSSL_VER: "1_1_1"
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
ONLY_CACHE: "false"
|
||||
MANUAL_CACHING: "0"
|
||||
PREPARE_PATH: "Telegram/build/prepare/prepare.py"
|
||||
AUTO_CACHING: "1"
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: cmd
|
||||
working-directory: Libraries
|
||||
working-directory: ${{ github.workspace }}
|
||||
|
||||
steps:
|
||||
- name: Prepare directories.
|
||||
run: |
|
||||
mkdir %userprofile%\TBuild\Libraries
|
||||
echo TBUILD=%userprofile%\TBuild>>%GITHUB_ENV%
|
||||
|
||||
- name: Get repository name.
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
|
||||
|
||||
- uses: ilammy/msvc-dev-cmd@v1.9.0
|
||||
name: x86 Native Tools Command Prompt.
|
||||
- uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||
name: Native Tools Command Prompt.
|
||||
with:
|
||||
arch: win32
|
||||
|
||||
- name: Set up environment paths.
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
echo "C:\\Strawberry\\perl\\bin\\" >> $GITHUB_PATH
|
||||
echo "C:\\Program Files\\NASM\\" >> $GITHUB_PATH
|
||||
echo "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\VC\\Auxiliary\\Build\\" >> $GITHUB_PATH
|
||||
|
||||
mkdir Libraries && cd Libraries
|
||||
echo "Convert unix path to win path."
|
||||
p=`pwd | sed 's#^/[d]#d:#g' |sed 's#/#\\\\#g'`
|
||||
echo "LibrariesPath=$p" >> $GITHUB_ENV
|
||||
|
||||
echo "QT=${QT_VER//./_}" >> $GITHUB_ENV
|
||||
|
||||
- name: Save msbuild version.
|
||||
run: |
|
||||
call vcvars32.bat
|
||||
msbuild -version > CACHE_KEY.txt
|
||||
arch: ${{ matrix.arch }}
|
||||
|
||||
- name: Clone.
|
||||
uses: actions/checkout@v2
|
||||
uses: LebedevRI/checkout@issue197
|
||||
with:
|
||||
submodules: recursive
|
||||
path: ${{ env.REPO_NAME }}
|
||||
|
||||
- name: Generate cache key.
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
curl -o $LibrariesPath/tg_owt-version.json https://api.github.com/repos/desktop-app/tg_owt/git/refs/heads/master
|
||||
curl -o $LibrariesPath/tg_angle-version.json https://api.github.com/repos/desktop-app/tg_angle/git/refs/heads/master
|
||||
echo $MANUAL_CACHING >> CACHE_KEY.txt
|
||||
if [ "$AUTO_CACHING" == "1" ]; then
|
||||
thisFile=$REPO_NAME/.github/workflows/win.yml
|
||||
echo `md5sum $thisFile | awk '{ print $1 }'` >> CACHE_KEY.txt
|
||||
fi
|
||||
echo "CACHE_KEY=`md5sum CACHE_KEY.txt | awk '{ print $1 }'`" >> $GITHUB_ENV
|
||||
path: ${{ env.TBUILD }}\${{ env.REPO_NAME }}
|
||||
|
||||
- name: Choco installs.
|
||||
run: |
|
||||
choco install --allow-empty-checksums --no-progress -y yasm
|
||||
choco install --no-progress -y nasm jom ninja
|
||||
python -m pip install pywin32
|
||||
choco install --no-progress -y nasm strawberryperl yasm jom ninja
|
||||
py -m pip install pywin32
|
||||
|
||||
- name: Install msys64.
|
||||
run: |
|
||||
mkdir %TBUILD%\ThirdParty
|
||||
xcopy /E /I C:\msys64 %TBUILD%\ThirdParty\msys64
|
||||
|
||||
- name: Set up environment paths.
|
||||
shell: bash
|
||||
run: |
|
||||
echo "C:\\Strawberry\\perl\\bin\\" >> $GITHUB_PATH
|
||||
echo "C:\\Program Files\\NASM\\" >> $GITHUB_PATH
|
||||
echo "C:\\ProgramData\\chocolatey\\lib\\ninja\\tools\\" >> $GITHUB_PATH
|
||||
|
||||
echo "Configurate git for cherry-picks."
|
||||
git config --global user.email "you@example.com"
|
||||
git config --global user.name "Sample"
|
||||
|
||||
- name: NuGet sources.
|
||||
run: |
|
||||
nuget sources Disable -Name "Microsoft Visual Studio Offline Packages"
|
||||
nuget sources Add -Source https://api.nuget.org/v3/index.json & exit 0
|
||||
|
||||
- name: Patches.
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
echo "Find necessary commit from doc."
|
||||
checkoutCommit=$(grep -A 1 "cd patches" $REPO_NAME/$PREPARE_PATH | sed -n 2p)
|
||||
cd $LibrariesPath
|
||||
git clone $GIT/desktop-app/patches.git
|
||||
cd patches
|
||||
eval $checkoutCommit
|
||||
|
||||
- name: LZMA.
|
||||
run: |
|
||||
git clone %GIT%/telegramdesktop/lzma.git
|
||||
cd lzma
|
||||
cd C\Util\LzmaLib
|
||||
msbuild -m LzmaLib.sln /property:Configuration=Debug
|
||||
|
||||
- name: OpenSSL cache.
|
||||
id: cache-openssl
|
||||
- name: Libraries cache.
|
||||
id: cache-libs
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/openssl
|
||||
key: ${{ runner.OS }}-${{ env.CACHE_KEY }}-${{ env.OPENSSL_VER }}
|
||||
- name: OpenSSL.
|
||||
if: steps.cache-openssl.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone -b OpenSSL_%OPENSSL_VER%-stable %GIT%/openssl/openssl.git
|
||||
cd openssl
|
||||
perl Configure no-shared no-tests debug-VC-WIN32
|
||||
nmake
|
||||
mkdir out.dbg
|
||||
move libcrypto.lib out.dbg
|
||||
move libssl.lib out.dbg
|
||||
move ossl_static.pdb out.dbg\ossl_static
|
||||
nmake clean
|
||||
move out.dbg\ossl_static out.dbg\ossl_static.pdb
|
||||
perl Configure no-shared no-tests VC-WIN32
|
||||
nmake
|
||||
mkdir out
|
||||
move libcrypto.lib out
|
||||
move libssl.lib out
|
||||
move ossl_static.pdb out
|
||||
path: ${{ env.TBUILD }}/Libraries
|
||||
key: ${{ runner.OS }}-libs
|
||||
|
||||
rmdir /S /Q test
|
||||
rmdir /S /Q .git
|
||||
|
||||
- name: Zlib.
|
||||
run: |
|
||||
git clone %GIT%/telegramdesktop/zlib.git
|
||||
cd zlib
|
||||
git checkout tdesktop
|
||||
cd contrib\vstudio\vc14
|
||||
msbuild -m zlibstat.vcxproj /property:Configuration=Debug
|
||||
|
||||
- name: MozJPEG.
|
||||
shell: cmd
|
||||
run: |
|
||||
git clone -b v4.0.3 %GIT%/mozilla/mozjpeg.git
|
||||
cd mozjpeg
|
||||
cmake . ^
|
||||
-G "Visual Studio 16 2019" ^
|
||||
-A Win32 ^
|
||||
-DWITH_JPEG8=ON ^
|
||||
-DPNG_SUPPORTED=OFF
|
||||
cmake --build . --config Debug
|
||||
|
||||
- name: OpenAL Soft cache.
|
||||
id: cache-openal
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/openal-soft
|
||||
key: ${{ runner.OS }}-openal-soft-${{ env.CACHE_KEY }}
|
||||
- name: OpenAL Soft.
|
||||
if: steps.cache-openal.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone -b openal-soft-1.21.0 --depth=1 %GIT%/kcat/openal-soft.git
|
||||
cd openal-soft\build
|
||||
cmake .. ^
|
||||
-G "Visual Studio 16 2019" ^
|
||||
-A Win32 ^
|
||||
-D LIBTYPE:STRING=STATIC ^
|
||||
-D FORCE_STATIC_VCRT=ON ^
|
||||
-D ALSOFT_BACKEND_DSOUND=OFF
|
||||
|
||||
msbuild -m OpenAL.vcxproj /property:Configuration=Debug
|
||||
|
||||
- name: Breakpad cache.
|
||||
id: cache-breakpad
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/breakpad
|
||||
key: ${{ runner.OS }}-breakpad-${{ env.CACHE_KEY }}-${{ hashFiles('**/breakpad.diff') }}
|
||||
- name: Breakpad.
|
||||
- name: Libraries.
|
||||
env:
|
||||
GYP_MSVS_OVERRIDE_PATH: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\'
|
||||
GYP_MSVS_VERSION: 2019
|
||||
if: steps.cache-breakpad.outputs.cache-hit != 'true'
|
||||
GYP_MSVS_OVERRIDE_PATH: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\'
|
||||
GYP_MSVS_VERSION: 2022
|
||||
run: |
|
||||
git clone https://chromium.googlesource.com/external/gyp
|
||||
cd gyp
|
||||
SET PATH=%cd%;%PATH%
|
||||
git checkout d6c5dd51dc
|
||||
git apply ../patches/gyp.diff
|
||||
|
||||
cd %LibrariesPath%
|
||||
|
||||
git clone https://chromium.googlesource.com/breakpad/breakpad
|
||||
cd breakpad
|
||||
git checkout dfcb7b6799
|
||||
git apply ../patches/breakpad.diff
|
||||
cd src
|
||||
git clone -b release-1.11.0 %GIT%/google/googletest testing
|
||||
cd client\windows
|
||||
call gyp --no-circular-check breakpad_client.gyp --format=ninja
|
||||
cd ..\..
|
||||
ninja -C out/Debug common crash_generation_client exception_handler
|
||||
ninja -C out/Release common crash_generation_client exception_handler
|
||||
cd tools\windows\dump_syms
|
||||
call gyp dump_syms.gyp --format=ninja
|
||||
cd ..\..\..
|
||||
ninja -C out/Release dump_syms
|
||||
|
||||
- name: Opus cache.
|
||||
id: cache-opus
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/opus
|
||||
key: ${{ runner.OS }}-opus-${{ env.CACHE_KEY }}
|
||||
- name: Opus.
|
||||
if: steps.cache-opus.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone -b v1.3.1 %GIT%/xiph/opus.git
|
||||
cd opus
|
||||
git cherry-pick 927de8453c
|
||||
cmake -B out . ^
|
||||
-A Win32 ^
|
||||
-DCMAKE_INSTALL_PREFIX=%LibrariesPath%/local/opus ^
|
||||
-DCMAKE_C_FLAGS_DEBUG="/MTd /Zi /Ob0 /Od /RTC1" ^
|
||||
-DCMAKE_C_FLAGS_RELEASE="/MT /O2 /Ob2 /DNDEBUG"
|
||||
cmake --build out --config Debug
|
||||
cmake --build out --config Release
|
||||
cmake --install out --config Release
|
||||
|
||||
- name: Rnnoise.
|
||||
run: |
|
||||
git clone %GIT%/desktop-app/rnnoise.git
|
||||
mkdir rnnoise\out
|
||||
cd rnnoise\out
|
||||
cmake -A Win32 ..
|
||||
cmake --build . --config Debug
|
||||
|
||||
- name: FFmpeg cache.
|
||||
id: cache-ffmpeg
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/ffmpeg
|
||||
key: ${{ runner.OS }}-ffmpeg-${{ env.CACHE_KEY }}-2-${{ hashFiles('**/build_ffmpeg_win.sh') }}
|
||||
- name: FFmpeg.
|
||||
if: steps.cache-ffmpeg.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
choco install --no-progress -y msys2
|
||||
|
||||
git clone %GIT%/FFmpeg/FFmpeg.git ffmpeg
|
||||
cd ffmpeg
|
||||
git checkout release/4.4
|
||||
set CHERE_INVOKING=enabled_from_arguments
|
||||
set MSYS2_PATH_TYPE=inherit
|
||||
call c:\tools\msys64\usr\bin\bash --login ../patches/build_ffmpeg_win.sh
|
||||
|
||||
rmdir /S /Q .git
|
||||
|
||||
- name: Angle cache.
|
||||
id: cache-angle
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/tg_angle
|
||||
key: ${{ runner.OS }}-angle-${{ env.CACHE_KEY }}-${{ hashFiles('**/tg_angle-version.json') }}
|
||||
- name: Angle.
|
||||
if: steps.cache-angle.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --recursive %GIT%/desktop-app/tg_angle.git
|
||||
mkdir tg_angle\out\Debug
|
||||
cd tg_angle\out\Debug
|
||||
cmake -G Ninja ^
|
||||
-DCMAKE_BUILD_TYPE=Debug ^
|
||||
-DTG_ANGLE_SPECIAL_TARGET=win64 ^
|
||||
-DTG_ANGLE_ZLIB_INCLUDE_PATH=%cd%/../../../zlib ../..
|
||||
ninja
|
||||
|
||||
:: Cleanup.
|
||||
cd %LibrariesPath%\tg_angle
|
||||
move out\Debug\tg_angle.lib tg_angle.lib
|
||||
rmdir /S /Q out
|
||||
mkdir out\Debug
|
||||
move tg_angle.lib out\Debug\tg_angle.lib
|
||||
|
||||
- name: Qt 5.15.2 cache.
|
||||
id: cache-qt
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/Qt-${{ env.QT_VER }}
|
||||
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_5_15_2/*') }}
|
||||
- name: Configure Qt 5.15.2.
|
||||
if: steps.cache-qt.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone git://code.qt.io/qt/qt5.git qt_%QT%
|
||||
cd qt_%QT%
|
||||
perl init-repository --module-subset=qtbase,qtimageformats,qtsvg
|
||||
git checkout v%QT_VER%
|
||||
git submodule update qtbase
|
||||
git submodule update qtimageformats
|
||||
git submodule update qtsvg
|
||||
cd qtbase
|
||||
for /r %%i in (..\..\patches\qtbase_%QT%\*) do git apply %%i
|
||||
cd ..
|
||||
|
||||
SET SSL=%LibrariesPath%\openssl
|
||||
SET SSL_LIBS=libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib
|
||||
|
||||
SET ANGLE=%LibrariesPath%\tg_angle
|
||||
SET ANGLE_LIBS=d3d9.lib dxgi.lib dxguid.lib
|
||||
|
||||
SET ZLIB=%LibrariesPath%\zlib\contrib\vstudio\vc14\x86
|
||||
|
||||
configure ^
|
||||
-prefix "%LibrariesPath%\Qt-%QT_VER%" ^
|
||||
-debug ^
|
||||
-force-debug-info ^
|
||||
-opensource ^
|
||||
-confirm-license ^
|
||||
-static ^
|
||||
-static-runtime ^
|
||||
-opengl es2 -no-angle ^
|
||||
-I "%ANGLE%\include" ^
|
||||
-D "GL_APICALL=" ^
|
||||
QMAKE_LIBS_OPENGL_ES2_DEBUG="%ANGLE%\out\Debug\tg_angle.lib %ZLIB%\ZlibStatDebug\zlibstat.lib %ANGLE_LIBS%" ^
|
||||
QMAKE_LIBS_OPENGL_ES2_RELEASE="%ANGLE%\out\Release\tg_angle.lib %ZLIB%\ZlibStatReleaseWithoutAsm\zlibstat.lib %ANGLE_LIBS%" ^
|
||||
-egl ^
|
||||
-D "EGLAPI=" ^
|
||||
-D "DESKTOP_APP_QT_STATIC_ANGLE=" ^
|
||||
QMAKE_LIBS_EGL_DEBUG="%ANGLE%\out\Debug\tg_angle.lib %ZLIB%\ZlibStatDebug\zlibstat.lib %ANGLE_LIBS% Gdi32.lib User32.lib" ^
|
||||
QMAKE_LIBS_EGL_RELEASE="%ANGLE%\out\Release\tg_angle.lib %ZLIB%\ZlibStatReleaseWithoutAsm\zlibstat.lib %ANGLE_LIBS% Gdi32.lib User32.lib" ^
|
||||
-openssl-linked ^
|
||||
-I "%SSL%\include" ^
|
||||
OPENSSL_LIBS_DEBUG="%SSL%\out.dbg\libssl.lib %SSL%\out.dbg\%SSL_LIBS%" ^
|
||||
OPENSSL_LIBS_RELEASE="%SSL%\out\libssl.lib %SSL%\out\%SSL_LIBS%" ^
|
||||
-I "%LibrariesPath%\mozjpeg" ^
|
||||
LIBJPEG_LIBS_DEBUG="%LibrariesPath%\mozjpeg\Debug\jpeg-static.lib" ^
|
||||
LIBJPEG_LIBS_RELEASE="%LibrariesPath%\mozjpeg\Release\jpeg-static.lib" ^
|
||||
-mp ^
|
||||
-nomake examples ^
|
||||
-nomake tests ^
|
||||
-platform win32-msvc
|
||||
|
||||
- name: Qt 5.15.2 build.
|
||||
if: steps.cache-qt.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd qt_%QT%
|
||||
|
||||
jom -j%NUMBER_OF_PROCESSORS%
|
||||
jom -j%NUMBER_OF_PROCESSORS% install
|
||||
|
||||
cd ..
|
||||
rmdir /S /Q qt_%QT%
|
||||
|
||||
- name: WebRTC cache.
|
||||
id: cache-webrtc
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/tg_owt
|
||||
key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}-${{ hashFiles('**/tg_owt-version.json') }}
|
||||
- name: WebRTC.
|
||||
if: steps.cache-webrtc.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --recursive %GIT%/desktop-app/tg_owt.git
|
||||
mkdir tg_owt\out\Debug
|
||||
cd tg_owt\out\Debug
|
||||
cmake -G Ninja ^
|
||||
-DCMAKE_BUILD_TYPE=Debug ^
|
||||
-DTG_OWT_SPECIAL_TARGET=win ^
|
||||
-DTG_OWT_BUILD_AUDIO_BACKENDS=OFF ^
|
||||
-DTG_OWT_LIBJPEG_INCLUDE_PATH=%cd%/../../../mozjpeg ^
|
||||
-DTG_OWT_OPENSSL_INCLUDE_PATH=%cd%/../../../openssl/include ^
|
||||
-DTG_OWT_OPUS_INCLUDE_PATH=%cd%/../../../opus/include ^
|
||||
-DTG_OWT_FFMPEG_INCLUDE_PATH=%cd%/../../../ffmpeg ^
|
||||
../..
|
||||
|
||||
ninja
|
||||
|
||||
:: Cleanup.
|
||||
cd %LibrariesPath%\tg_owt
|
||||
move out\Debug\tg_owt.lib tg_owt.lib
|
||||
rmdir /S /Q out
|
||||
mkdir out\Debug
|
||||
move tg_owt.lib out\Debug\tg_owt.lib
|
||||
C:
|
||||
cd %TBUILD%
|
||||
%REPO_NAME%/Telegram/build/prepare/win.bat skip-release silent
|
||||
|
||||
- name: Read defines.
|
||||
shell: bash
|
||||
@@ -438,16 +137,21 @@ jobs:
|
||||
echo "TDESKTOP_BUILD_DEFINE=$DEFINE" >> $GITHUB_ENV
|
||||
|
||||
- name: Free up some disk space.
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: del /S *.pdb
|
||||
run: |
|
||||
C:
|
||||
cd %TBUILD%
|
||||
del /S Libraries\*.pdb
|
||||
del /S Libraries\*.pch
|
||||
del /S Libraries\*.obj
|
||||
|
||||
- name: Telegram Desktop build.
|
||||
if: env.ONLY_CACHE == 'false'
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
cd %REPO_NAME%\Telegram
|
||||
C:
|
||||
cd %TBUILD%\%REPO_NAME%\Telegram
|
||||
|
||||
call configure.bat ^
|
||||
${{ matrix.arch }} ^
|
||||
-D TDESKTOP_API_TEST=ON ^
|
||||
-D DESKTOP_APP_USE_PACKAGED=OFF ^
|
||||
-D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF ^
|
||||
@@ -455,20 +159,17 @@ jobs:
|
||||
%TDESKTOP_BUILD_DEFINE% ^
|
||||
-DCMAKE_SYSTEM_VERSION=%SDK%
|
||||
|
||||
call vcvars32.bat
|
||||
cd ..\out
|
||||
msbuild -m Telegram.sln /nologo /p:Configuration=Debug,Platform=Win32
|
||||
msbuild -m Telegram.sln /p:Configuration=Debug,Platform=${{ matrix.arch }},DebugSymbols=false,DebugType=none
|
||||
|
||||
- name: Move artifact.
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
cd %REPO_NAME%\out\Debug
|
||||
mkdir artifact
|
||||
move Telegram.exe artifact/
|
||||
move %TBUILD%\%REPO_NAME%\out\Debug\Telegram.exe artifact/
|
||||
- uses: actions/upload-artifact@master
|
||||
name: Upload artifact.
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: ${{ env.REPO_NAME }}\out\Debug\artifact\
|
||||
path: artifact\
|
||||
|
||||
12
.gitmodules
vendored
@@ -52,9 +52,6 @@
|
||||
[submodule "Telegram/lib_qr"]
|
||||
path = Telegram/lib_qr
|
||||
url = https://github.com/desktop-app/lib_qr.git
|
||||
[submodule "Telegram/ThirdParty/libdbusmenu-qt"]
|
||||
path = Telegram/ThirdParty/libdbusmenu-qt
|
||||
url = https://github.com/desktop-app/libdbusmenu-qt.git
|
||||
[submodule "Telegram/ThirdParty/hunspell"]
|
||||
path = Telegram/ThirdParty/hunspell
|
||||
url = https://github.com/hunspell/hunspell
|
||||
@@ -94,3 +91,12 @@
|
||||
[submodule "Telegram/ThirdParty/dispatch"]
|
||||
path = Telegram/ThirdParty/dispatch
|
||||
url = https://github.com/apple/swift-corelibs-libdispatch
|
||||
[submodule "Telegram/ThirdParty/extra-cmake-modules"]
|
||||
path = Telegram/ThirdParty/extra-cmake-modules
|
||||
url = https://github.com/KDE/extra-cmake-modules.git
|
||||
[submodule "Telegram/ThirdParty/plasma-wayland-protocols"]
|
||||
path = Telegram/ThirdParty/plasma-wayland-protocols
|
||||
url = https://github.com/KDE/plasma-wayland-protocols.git
|
||||
[submodule "Telegram/ThirdParty/wayland-protocols"]
|
||||
path = Telegram/ThirdParty/wayland-protocols
|
||||
url = https://github.com/gitlab-freedesktop-mirrors/wayland-protocols.git
|
||||
|
||||
2
LEGAL
@@ -1,7 +1,7 @@
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
Copyright (c) 2014-2021 The Telegram Desktop Authors.
|
||||
Copyright (c) 2014-2022 The Telegram Desktop Authors.
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -147,6 +147,8 @@ PRIVATE
|
||||
api/api_text_entities.h
|
||||
api/api_toggling_media.cpp
|
||||
api/api_toggling_media.h
|
||||
api/api_unread_things.cpp
|
||||
api/api_unread_things.h
|
||||
api/api_updates.cpp
|
||||
api/api_updates.h
|
||||
api/api_user_privacy.cpp
|
||||
@@ -525,33 +527,16 @@ PRIVATE
|
||||
editor/controllers/controllers.h
|
||||
editor/controllers/stickers_panel_controller.cpp
|
||||
editor/controllers/stickers_panel_controller.h
|
||||
editor/controllers/undo_controller.cpp
|
||||
editor/controllers/undo_controller.h
|
||||
editor/editor_crop.cpp
|
||||
editor/editor_crop.h
|
||||
editor/editor_paint.cpp
|
||||
editor/editor_paint.h
|
||||
editor/photo_editor.cpp
|
||||
editor/photo_editor.h
|
||||
editor/photo_editor_common.cpp
|
||||
editor/photo_editor_common.h
|
||||
editor/photo_editor_content.cpp
|
||||
editor/photo_editor_content.h
|
||||
editor/photo_editor_controls.cpp
|
||||
editor/photo_editor_controls.h
|
||||
editor/photo_editor_inner_common.h
|
||||
editor/photo_editor_layer_widget.cpp
|
||||
editor/photo_editor_layer_widget.h
|
||||
editor/scene/scene.cpp
|
||||
editor/scene/scene.h
|
||||
editor/scene/scene_item_base.cpp
|
||||
editor/scene/scene_item_base.h
|
||||
editor/scene/scene_item_canvas.cpp
|
||||
editor/scene/scene_item_canvas.h
|
||||
editor/scene/scene_item_image.cpp
|
||||
editor/scene/scene_item_image.h
|
||||
editor/scene/scene_item_line.cpp
|
||||
editor/scene/scene_item_line.h
|
||||
editor/scene/scene_item_sticker.cpp
|
||||
editor/scene/scene_item_sticker.h
|
||||
export/export_manager.cpp
|
||||
@@ -655,6 +640,8 @@ PRIVATE
|
||||
history/view/history_view_pinned_section.h
|
||||
history/view/history_view_pinned_tracker.cpp
|
||||
history/view/history_view_pinned_tracker.h
|
||||
history/view/history_view_react_animation.cpp
|
||||
history/view/history_view_react_animation.h
|
||||
history/view/history_view_react_button.cpp
|
||||
history/view/history_view_react_button.h
|
||||
history/view/history_view_reactions.cpp
|
||||
@@ -701,6 +688,8 @@ PRIVATE
|
||||
history/history_message.h
|
||||
history/history_service.cpp
|
||||
history/history_service.h
|
||||
history/history_unread_things.cpp
|
||||
history/history_unread_things.h
|
||||
history/history_widget.cpp
|
||||
history/history_widget.h
|
||||
info/info_content_widget.cpp
|
||||
@@ -948,6 +937,8 @@ PRIVATE
|
||||
platform/linux/file_utilities_linux.h
|
||||
platform/linux/launcher_linux.cpp
|
||||
platform/linux/launcher_linux.h
|
||||
platform/linux/integration_linux.cpp
|
||||
platform/linux/integration_linux.h
|
||||
platform/linux/main_window_linux.cpp
|
||||
platform/linux/main_window_linux.h
|
||||
platform/linux/notifications_manager_linux_dummy.cpp
|
||||
@@ -959,6 +950,8 @@ PRIVATE
|
||||
platform/mac/file_utilities_mac.h
|
||||
platform/mac/launcher_mac.mm
|
||||
platform/mac/launcher_mac.h
|
||||
platform/mac/integration_mac.mm
|
||||
platform/mac/integration_mac.h
|
||||
platform/mac/mac_iconv_helper.c
|
||||
platform/mac/main_window_mac.mm
|
||||
platform/mac/main_window_mac.h
|
||||
@@ -993,6 +986,8 @@ PRIVATE
|
||||
platform/win/file_utilities_win.h
|
||||
platform/win/launcher_win.cpp
|
||||
platform/win/launcher_win.h
|
||||
platform/win/integration_win.cpp
|
||||
platform/win/integration_win.h
|
||||
platform/win/main_window_win.cpp
|
||||
platform/win/main_window_win.h
|
||||
platform/win/notifications_manager_win.cpp
|
||||
@@ -1003,8 +998,6 @@ PRIVATE
|
||||
platform/win/windows_app_user_model_id.h
|
||||
platform/win/windows_dlls.cpp
|
||||
platform/win/windows_dlls.h
|
||||
platform/win/windows_event_filter.cpp
|
||||
platform/win/windows_event_filter.h
|
||||
platform/win/windows_autostart_task.cpp
|
||||
platform/win/windows_autostart_task.h
|
||||
platform/win/windows_toast_activator.cpp
|
||||
@@ -1012,6 +1005,8 @@ PRIVATE
|
||||
platform/platform_audio.h
|
||||
platform/platform_file_utilities.h
|
||||
platform/platform_launcher.h
|
||||
platform/platform_integration.cpp
|
||||
platform/platform_integration.h
|
||||
platform/platform_main_window.h
|
||||
platform/platform_notifications_manager.h
|
||||
platform/platform_specific.h
|
||||
@@ -1036,6 +1031,8 @@ PRIVATE
|
||||
settings/settings_codes.h
|
||||
settings/settings_common.cpp
|
||||
settings/settings_common.h
|
||||
settings/settings_experimental.cpp
|
||||
settings/settings_experimental.h
|
||||
settings/settings_folders.cpp
|
||||
settings/settings_folders.h
|
||||
settings/settings_information.cpp
|
||||
@@ -1202,8 +1199,6 @@ PRIVATE
|
||||
window/themes/window_themes_generate_name.h
|
||||
apiwrap.cpp
|
||||
apiwrap.h
|
||||
app.cpp
|
||||
app.h
|
||||
config.h
|
||||
facades.cpp
|
||||
facades.h
|
||||
@@ -1343,8 +1338,6 @@ else()
|
||||
if (NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
desktop-app::external_statusnotifieritem
|
||||
desktop-app::external_dbusmenu_qt
|
||||
desktop-app::external_glibmm
|
||||
desktop-app::external_glib
|
||||
)
|
||||
|
||||
|
Before Width: | Height: | Size: 305 B |
|
Before Width: | Height: | Size: 524 B |
|
Before Width: | Height: | Size: 820 B |
|
Before Width: | Height: | Size: 385 B |
|
Before Width: | Height: | Size: 873 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 230 B |
|
Before Width: | Height: | Size: 393 B |
|
Before Width: | Height: | Size: 616 B |
|
Before Width: | Height: | Size: 333 B |
|
Before Width: | Height: | Size: 552 B |
|
Before Width: | Height: | Size: 889 B |
|
Before Width: | Height: | Size: 173 B |
|
Before Width: | Height: | Size: 304 B |
|
Before Width: | Height: | Size: 545 B |
|
Before Width: | Height: | Size: 228 B |
|
Before Width: | Height: | Size: 420 B |
|
Before Width: | Height: | Size: 661 B |
|
Before Width: | Height: | Size: 290 B |
|
Before Width: | Height: | Size: 499 B |
|
Before Width: | Height: | Size: 846 B |
|
Before Width: | Height: | Size: 420 B |
|
Before Width: | Height: | Size: 715 B |
|
Before Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/dialogs/dialogs_mention.png
Normal file
|
After Width: | Height: | Size: 569 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_mention@2x.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Telegram/Resources/icons/dialogs/dialogs_mention@3x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/dialogs/dialogs_reaction.png
Normal file
|
After Width: | Height: | Size: 340 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_reaction@2x.png
Normal file
|
After Width: | Height: | Size: 565 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_reaction@3x.png
Normal file
|
After Width: | Height: | Size: 814 B |
|
Before Width: | Height: | Size: 453 B After Width: | Height: | Size: 463 B |
|
Before Width: | Height: | Size: 973 B After Width: | Height: | Size: 819 B |
|
Before Width: | Height: | Size: 946 B After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 583 B After Width: | Height: | Size: 727 B |
|
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.3 KiB |
BIN
Telegram/Resources/icons/history_unread_reaction.png
Normal file
|
After Width: | Height: | Size: 635 B |
BIN
Telegram/Resources/icons/history_unread_reaction@2x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/history_unread_reaction@3x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
Telegram/Resources/icons/info/edit/group_manage_reactions.png
Normal file
|
After Width: | Height: | Size: 1016 B |
BIN
Telegram/Resources/icons/info/edit/group_manage_reactions@2x.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
Telegram/Resources/icons/info/edit/group_manage_reactions@3x.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 823 B After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 11 KiB |
@@ -348,6 +348,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_notification_sample" = "This is a sample notification";
|
||||
"lng_notification_reminder" = "Reminder";
|
||||
|
||||
"lng_reaction_text" = "{reaction} to your «{text}»";
|
||||
"lng_reaction_notext" = "{reaction} to your message";
|
||||
"lng_reaction_photo" = "{reaction} to your photo";
|
||||
"lng_reaction_video" = "{reaction} to your video";
|
||||
"lng_reaction_video_message" = "{reaction} to your video message";
|
||||
"lng_reaction_document" = "{reaction} to your file";
|
||||
"lng_reaction_sticker" = "{reaction} to your {emoji}sticker";
|
||||
"lng_reaction_voice_message" = "{reaction} to your voice message";
|
||||
"lng_reaction_contact" = "{reaction} to your contact {name}";
|
||||
"lng_reaction_location" = "{reaction} to your map";
|
||||
"lng_reaction_live_location" = "{reaction} to your live location";
|
||||
"lng_reaction_poll" = "{reaction} to your poll {title}";
|
||||
"lng_reaction_quiz" = "{reaction} to your quiz {title}";
|
||||
"lng_reaction_game" = "{reaction} to your game";
|
||||
"lng_reaction_invoice" = "{reaction} to your invoice";
|
||||
"lng_reaction_gif" = "{reaction} to your GIF";
|
||||
|
||||
"lng_settings_section_general" = "General";
|
||||
"lng_settings_change_lang" = "Change language";
|
||||
"lng_languages" = "Languages";
|
||||
@@ -372,9 +389,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_auto_start_disabled_uwp" = "Starting with the system was disabled in Windows Settings.\n\nPlease enable Telegram Desktop in the Startup Apps Settings.";
|
||||
"lng_settings_open_system_settings" = "Open Settings";
|
||||
"lng_settings_add_sendto" = "Place Telegram in \"Send to\" menu";
|
||||
"lng_settings_mac_warn_before_quit" = "Show warning before quitting with {text}";
|
||||
"lng_settings_section_scale" = "Interface Scale";
|
||||
"lng_settings_scale_auto" = "Auto ({cur})";
|
||||
|
||||
"lng_settings_experimental" = "Experimental settings";
|
||||
"lng_settings_experimental_about" = "Warning! Those are experimental settings. Some may not work. Others may break the app. Any of them may disappear in the next version without a trace. Use at your own risk.";
|
||||
"lng_settings_experimental_restore" = "Restore default values";
|
||||
"lng_settings_experimental_irrelevant" = "This option isn't relevant for your system.";
|
||||
|
||||
"lng_settings_section_chat_settings" = "Chat Settings";
|
||||
"lng_settings_replace_emojis" = "Replace emoji";
|
||||
"lng_settings_suggest_emoji" = "Suggest emoji replacements";
|
||||
@@ -440,7 +463,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_phone_label" = "Phone number";
|
||||
"lng_settings_username_add" = "Add username";
|
||||
"lng_settings_close_sure" = "Are you sure you want to close this page? You didn't save your changes.";
|
||||
//"lng_settings_peer_to_peer" = "Peer-to-Peer";
|
||||
"lng_settings_peer_to_peer_about" = "Disabling peer-to-peer will relay all calls through Telegram servers to avoid revealing your IP address, but may slightly decrease audio quality.";
|
||||
"lng_settings_advanced" = "Advanced";
|
||||
"lng_settings_stickers_emoji" = "Stickers and emoji";
|
||||
@@ -714,7 +736,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_sure_logout" = "Are you sure you want to log out?";
|
||||
|
||||
"lng_settings_need_restart" = "You need to restart for applying some of the new settings. Restart now?";
|
||||
"lng_settings_restart_now" = "RESTART";
|
||||
"lng_settings_restart_now" = "Restart";
|
||||
"lng_settings_restart_later" = "Later";
|
||||
|
||||
"lng_sessions_header" = "Current session";
|
||||
"lng_sessions_other_header" = "Active sessions";
|
||||
@@ -917,8 +940,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_profile_files_header" = "Files";
|
||||
"lng_profile_audios#one" = "{count} voice message";
|
||||
"lng_profile_audios#other" = "{count} voice messages";
|
||||
//"lng_profile_rounds#one" = "{count} video message";
|
||||
//"lng_profile_rounds#other" = "{count} video messages";
|
||||
"lng_profile_audios_header" = "Voice messages";
|
||||
"lng_profile_shared_links#one" = "{count} shared link";
|
||||
"lng_profile_shared_links#other" = "{count} shared links";
|
||||
@@ -978,8 +999,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_media_selected_file#other" = "{count} Files";
|
||||
"lng_media_selected_audio#one" = "{count} Voice message";
|
||||
"lng_media_selected_audio#other" = "{count} Voice messages";
|
||||
//"lng_media_selected_round#one" = "{count} Video message";
|
||||
//"lng_media_selected_round#other" = "{count} Video messages";
|
||||
"lng_media_selected_link#one" = "{count} Shared link";
|
||||
"lng_media_selected_link#other" = "{count} Shared links";
|
||||
"lng_media_photo_empty" = "No photos here yet";
|
||||
@@ -1259,10 +1278,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_you_proximity_reached" = "You are now within {distance} from {user}";
|
||||
"lng_action_you_theme_changed" = "You changed chat theme to {emoji}";
|
||||
"lng_action_theme_changed" = "{from} changed chat theme to {emoji}";
|
||||
"lng_action_theme_changed_channel" = "Channel theme changed to {emoji}";
|
||||
"lng_action_you_theme_disabled" = "You disabled chat theme";
|
||||
"lng_action_theme_disabled" = "{from} disabled chat theme";
|
||||
"lng_action_theme_disabled_channel" = "Channel theme disabled";
|
||||
"lng_action_proximity_distance_m#one" = "{count} meter";
|
||||
"lng_action_proximity_distance_m#other" = "{count} metres";
|
||||
"lng_action_proximity_distance_km#one" = "{count} km";
|
||||
@@ -1729,7 +1746,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_send_message" = "Send message";
|
||||
"lng_context_view_group" = "View group info";
|
||||
"lng_context_view_channel" = "View channel info";
|
||||
//"lng_context_view_feed_info" = "View feed info";
|
||||
"lng_context_hide_psa" = "Hide this announcement";
|
||||
"lng_context_pin_to_top" = "Pin to top";
|
||||
"lng_context_unpin_from_top" = "Unpin from top";
|
||||
@@ -1739,12 +1755,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_mark_read_all" = "Mark all chats as read";
|
||||
"lng_context_mark_read_all_sure" = "Are you sure you want to mark all chats as read?";
|
||||
"lng_context_mark_read_mentions_all" = "Mark all mentions as read";
|
||||
"lng_context_mark_read_reactions_all" = "Read all reactions";
|
||||
"lng_context_archive_expand" = "Expand";
|
||||
"lng_context_archive_collapse" = "Collapse";
|
||||
"lng_context_archive_to_menu" = "Move to main menu";
|
||||
"lng_context_archive_to_list" = "Move to chats list";
|
||||
"lng_context_archive_to_menu_info" = "Archive moved to the main menu!\nYou can return it from the context menu of the archive button.";
|
||||
|
||||
"lng_context_mute" = "Mute notifications";
|
||||
"lng_context_unmute" = "Unmute";
|
||||
|
||||
"lng_context_promote_admin" = "Promote to admin";
|
||||
"lng_context_edit_permissions" = "Edit permissions";
|
||||
"lng_context_restrict_user" = "Restrict user";
|
||||
@@ -1807,6 +1827,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_seen_reacted#other" = "{count} Reacted";
|
||||
"lng_context_seen_reacted_none" = "Nobody Reacted";
|
||||
"lng_context_seen_reacted_all" = "Show All Reactions";
|
||||
"lng_context_set_as_quick" = "Set As Quick";
|
||||
|
||||
"lng_send_image_empty" = "Could not send an empty file: {name}";
|
||||
"lng_send_image_too_large" = "Could not send a file, because it is larger than 1500 MB: {name}";
|
||||
@@ -1829,7 +1850,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_forward_share_contact" = "Share contact to {recipient}?";
|
||||
"lng_forward_share_cant" = "Sorry, no way to share contact here :(";
|
||||
|
||||
|
||||
"lng_forward_send_files_cant" = "Sorry, no way to send media here :(";
|
||||
"lng_forward_send" = "Send";
|
||||
"lng_forward_messages#one" = "{count} forwarded message";
|
||||
@@ -1896,6 +1916,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_delete_for_me_chat_hint#other" = "This will delete them just for you, not for other participants of the chat.";
|
||||
"lng_delete_for_me_hint#one" = "This will delete it just for you.";
|
||||
"lng_delete_for_me_hint#other" = "This will delete them just for you.";
|
||||
"lng_delete_clear_for_me" = "This will clear history just for you, not for other participants of the chat.";
|
||||
"lng_edit_auto_delete_settings" = "Edit Auto-Delete Settings";
|
||||
"lng_enable_auto_delete" = "Enable Auto-Delete";
|
||||
"lng_selected_unsend_about_user_one" = "You can also delete the message you sent from {user}'s inbox by checking \"Unsend my messages\".";
|
||||
@@ -1910,6 +1931,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_box_delete" = "Delete";
|
||||
"lng_box_leave" = "Leave";
|
||||
|
||||
"lng_upload_sure_stop" = "Are you sure you want to stop uploading your files?\n\nIf you do, you'll need to start over.";
|
||||
"lng_upload_show_file" = "Show file";
|
||||
|
||||
"lng_about_version" = "version {version}";
|
||||
"lng_about_text1" = "Official free messaging app based on {api_link}\nfor speed and security.";
|
||||
"lng_about_text1_api" = "Telegram API";
|
||||
@@ -1977,6 +2001,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_formatting_link_url" = "URL";
|
||||
"lng_formatting_link_create" = "Create";
|
||||
|
||||
"lng_text_copied" = "Text copied to clipboard.";
|
||||
|
||||
"lng_spellchecker_submenu" = "Spelling";
|
||||
"lng_spellchecker_add" = "Add to Dictionary";
|
||||
"lng_spellchecker_remove" = "Remove from Dictionary";
|
||||
@@ -3044,6 +3070,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_mac_menu_preferences" = "Preferences...";
|
||||
"lng_mac_menu_quit_telegram" = "Quit {telegram}";
|
||||
"lng_mac_menu_about_telegram" = "About {telegram}";
|
||||
//"lng_mac_menu_warn_before_quit" = "Warn Before Quitting ({text})";
|
||||
"lng_mac_menu_file" = "File";
|
||||
"lng_mac_menu_logout" = "Log Out";
|
||||
"lng_mac_menu_edit" = "Edit";
|
||||
@@ -3070,4 +3097,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_mac_touchbar_favorite_stickers" = "Favorite stickers";
|
||||
|
||||
"lng_mac_hold_to_quit" = "Hold {text} to Quit";
|
||||
|
||||
// Keys finished
|
||||
|
||||
@@ -190,7 +190,7 @@ messageActionGroupCallScheduled#b3a07661 call:InputGroupCall schedule_date:int =
|
||||
messageActionSetChatTheme#aa786345 emoticon:string = MessageAction;
|
||||
messageActionChatJoinedByRequest#ebbca3cb = MessageAction;
|
||||
|
||||
dialog#2c171f72 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
|
||||
dialog#a8edd0f5 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
|
||||
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
||||
|
||||
photoEmpty#2331b22d id:long = Photo;
|
||||
@@ -566,7 +566,7 @@ inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
|
||||
inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
|
||||
inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet;
|
||||
|
||||
stickerSet#d7df217a flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int count:int hash:int = StickerSet;
|
||||
stickerSet#d7df217a flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true gifs:flags.6?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int count:int hash:int = StickerSet;
|
||||
|
||||
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
|
||||
messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
|
||||
@@ -1307,17 +1307,20 @@ auth.loggedOut#c3a2835f flags:# future_auth_token:flags.0?bytes = auth.LoggedOut
|
||||
|
||||
reactionCount#6fb250d1 flags:# chosen:flags.0?true reaction:string count:int = ReactionCount;
|
||||
|
||||
messageReactions#87b6e36 flags:# min:flags.0?true can_see_list:flags.2?true results:Vector<ReactionCount> recent_reactons:flags.1?Vector<MessageUserReaction> = MessageReactions;
|
||||
messageReactions#4f2b9479 flags:# min:flags.0?true can_see_list:flags.2?true results:Vector<ReactionCount> recent_reactions:flags.1?Vector<MessagePeerReaction> = MessageReactions;
|
||||
|
||||
messageUserReaction#932844fa user_id:long reaction:string = MessageUserReaction;
|
||||
messages.messageReactionsList#31bd492d flags:# count:int reactions:Vector<MessagePeerReaction> chats:Vector<Chat> users:Vector<User> next_offset:flags.0?string = messages.MessageReactionsList;
|
||||
|
||||
messages.messageReactionsList#a366923c flags:# count:int reactions:Vector<MessageUserReaction> users:Vector<User> next_offset:flags.0?string = messages.MessageReactionsList;
|
||||
|
||||
availableReaction#21d7c4b flags:# inactive:flags.0?true reaction:string title:string static_icon:Document appear_animation:Document select_animation:Document activate_animation:Document effect_animation:Document = AvailableReaction;
|
||||
availableReaction#c077ec01 flags:# inactive:flags.0?true reaction:string title:string static_icon:Document appear_animation:Document select_animation:Document activate_animation:Document effect_animation:Document around_animation:flags.1?Document center_icon:flags.1?Document = AvailableReaction;
|
||||
|
||||
messages.availableReactionsNotModified#9f071957 = messages.AvailableReactions;
|
||||
messages.availableReactions#768e3aad hash:int reactions:Vector<AvailableReaction> = messages.AvailableReactions;
|
||||
|
||||
messages.translateNoResult#67ca4737 = messages.TranslatedText;
|
||||
messages.translateResultText#a214f7d0 text:string = messages.TranslatedText;
|
||||
|
||||
messagePeerReaction#51b67eff flags:# big:flags.0?true unread:flags.1?true peer_id:Peer reaction:string = MessagePeerReaction;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@@ -1596,12 +1599,15 @@ messages.hideChatJoinRequest#7fe7e815 flags:# approved:flags.0?true peer:InputPe
|
||||
messages.hideAllChatJoinRequests#e085f4ea flags:# approved:flags.0?true peer:InputPeer link:flags.1?string = Updates;
|
||||
messages.toggleNoForwards#b11eafa2 peer:InputPeer enabled:Bool = Updates;
|
||||
messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool;
|
||||
messages.sendReaction#25690ce4 flags:# peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
|
||||
messages.sendReaction#25690ce4 flags:# big:flags.1?true peer:InputPeer msg_id:int reaction:flags.0?string = Updates;
|
||||
messages.getMessagesReactions#8bba90e6 peer:InputPeer id:Vector<int> = Updates;
|
||||
messages.getMessageReactionsList#e0ee6b77 flags:# peer:InputPeer id:int reaction:flags.0?string offset:flags.1?string limit:int = messages.MessageReactionsList;
|
||||
messages.setChatAvailableReactions#14050ea6 peer:InputPeer available_reactions:Vector<string> = Updates;
|
||||
messages.getAvailableReactions#18dea0ac hash:int = messages.AvailableReactions;
|
||||
messages.setDefaultReaction#d960c4d4 reaction:string = Bool;
|
||||
messages.translateText#24ce6dee flags:# peer:flags.0?InputPeer msg_id:flags.0?int text:flags.1?string from_lang:flags.2?string to_lang:string = messages.TranslatedText;
|
||||
messages.getUnreadReactions#e85bae1a peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readReactions#82e251d7 peer:InputPeer = messages.AffectedHistory;
|
||||
|
||||
updates.getState#edd4882a = updates.State;
|
||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||
@@ -1750,4 +1756,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
|
||||
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
|
||||
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
|
||||
|
||||
// LAYER 136
|
||||
// LAYER 138
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="3.3.2.0" />
|
||||
Version="3.5.1.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 3,3,2,0
|
||||
PRODUCTVERSION 3,3,2,0
|
||||
FILEVERSION 3,5,1,0
|
||||
PRODUCTVERSION 3,5,1,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "3.3.2.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "FileVersion", "3.5.1.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2022"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "3.3.2.0"
|
||||
VALUE "ProductVersion", "3.5.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 3,3,2,0
|
||||
PRODUCTVERSION 3,3,2,0
|
||||
FILEVERSION 3,5,1,0
|
||||
PRODUCTVERSION 3,5,1,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "3.3.2.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "FileVersion", "3.5.1.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2022"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "3.3.2.0"
|
||||
VALUE "ProductVersion", "3.5.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -252,12 +252,13 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) {
|
||||
|
||||
if (_photo) {
|
||||
if (const auto image = _photo->image(Data::PhotoSize::Small)) {
|
||||
const auto size = st::confirmInvitePhotoSize;
|
||||
p.drawPixmap(
|
||||
(width() - st::confirmInvitePhotoSize) / 2,
|
||||
(width() - size) / 2,
|
||||
st::confirmInvitePhotoTop,
|
||||
image->pixCircled(
|
||||
st::confirmInvitePhotoSize,
|
||||
st::confirmInvitePhotoSize));
|
||||
image->pix(
|
||||
{ size, size },
|
||||
{ .options = Images::Option::RoundCircle }));
|
||||
}
|
||||
} else if (_photoEmpty) {
|
||||
_photoEmpty->paint(
|
||||
|
||||
@@ -27,7 +27,7 @@ EntitiesInText EntitiesFromMTP(
|
||||
for (const auto &entity : entities) {
|
||||
switch (entity.type()) {
|
||||
case mtpc_messageEntityUrl: { auto &d = entity.c_messageEntityUrl(); result.push_back({ EntityType::Url, d.voffset().v, d.vlength().v }); } break;
|
||||
case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back({ EntityType::CustomUrl, d.voffset().v, d.vlength().v, Clean(qs(d.vurl())) }); } break;
|
||||
case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back({ EntityType::CustomUrl, d.voffset().v, d.vlength().v, qs(d.vurl()) }); } break;
|
||||
case mtpc_messageEntityEmail: { auto &d = entity.c_messageEntityEmail(); result.push_back({ EntityType::Email, d.voffset().v, d.vlength().v }); } break;
|
||||
case mtpc_messageEntityHashtag: { auto &d = entity.c_messageEntityHashtag(); result.push_back({ EntityType::Hashtag, d.voffset().v, d.vlength().v }); } break;
|
||||
case mtpc_messageEntityCashtag: { auto &d = entity.c_messageEntityCashtag(); result.push_back({ EntityType::Cashtag, d.voffset().v, d.vlength().v }); } break;
|
||||
@@ -71,7 +71,7 @@ EntitiesInText EntitiesFromMTP(
|
||||
case mtpc_messageEntityUnderline: { auto &d = entity.c_messageEntityUnderline(); result.push_back({ EntityType::Underline, d.voffset().v, d.vlength().v }); } break;
|
||||
case mtpc_messageEntityStrike: { auto &d = entity.c_messageEntityStrike(); result.push_back({ EntityType::StrikeOut, d.voffset().v, d.vlength().v }); } break;
|
||||
case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back({ EntityType::Code, d.voffset().v, d.vlength().v }); } break;
|
||||
case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, Clean(qs(d.vlanguage())) }); } break;
|
||||
case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, qs(d.vlanguage()) }); } break;
|
||||
case mtpc_messageEntityBankCard: break; // Skipping cards.
|
||||
case mtpc_messageEntitySpoiler: { auto &d = entity.c_messageEntitySpoiler(); result.push_back({ EntityType::Spoiler, d.voffset().v, d.vlength().v }); } break;
|
||||
// #TODO entities
|
||||
|
||||
138
Telegram/SourceFiles/api/api_unread_things.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_unread_things.h"
|
||||
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
constexpr auto kPreloadIfLess = 5;
|
||||
constexpr auto kFirstRequestLimit = 10;
|
||||
constexpr auto kNextRequestLimit = 100;
|
||||
|
||||
} // namespace
|
||||
|
||||
UnreadThings::UnreadThings(not_null<ApiWrap*> api) : _api(api) {
|
||||
}
|
||||
|
||||
bool UnreadThings::trackMentions(PeerData *peer) const {
|
||||
return peer && (peer->isChat() || peer->isMegagroup());
|
||||
}
|
||||
|
||||
bool UnreadThings::trackReactions(PeerData *peer) const {
|
||||
return trackMentions(peer) || (peer && peer->isUser());
|
||||
}
|
||||
|
||||
void UnreadThings::preloadEnough(History *history) {
|
||||
if (!history) {
|
||||
return;
|
||||
}
|
||||
if (trackMentions(history->peer)) {
|
||||
preloadEnoughMentions(history);
|
||||
}
|
||||
if (trackReactions(history->peer)) {
|
||||
preloadEnoughReactions(history);
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::mediaAndMentionsRead(
|
||||
const base::flat_set<MsgId> &readIds,
|
||||
ChannelData *channel) {
|
||||
for (const auto &msgId : readIds) {
|
||||
_api->requestMessageData(channel, msgId, [=] {
|
||||
const auto item = channel
|
||||
? _api->session().data().message(channel->id, msgId)
|
||||
: _api->session().data().nonChannelMessage(msgId);
|
||||
if (item && item->mentionsMe()) {
|
||||
item->markMediaAndMentionRead();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::preloadEnoughMentions(not_null<History*> history) {
|
||||
const auto fullCount = history->unreadMentions().count();
|
||||
const auto loadedCount = history->unreadMentions().loadedCount();
|
||||
const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount);
|
||||
if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) {
|
||||
requestMentions(history, loadedCount);
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::preloadEnoughReactions(not_null<History*> history) {
|
||||
const auto fullCount = history->unreadReactions().count();
|
||||
const auto loadedCount = history->unreadReactions().loadedCount();
|
||||
const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount);
|
||||
if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) {
|
||||
requestReactions(history, loadedCount);
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::requestMentions(not_null<History*> history, int loaded) {
|
||||
if (_mentionsRequests.contains(history)) {
|
||||
return;
|
||||
}
|
||||
const auto offsetId = std::max(
|
||||
history->unreadMentions().maxLoaded(),
|
||||
MsgId(1));
|
||||
const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit;
|
||||
const auto addOffset = loaded ? -(limit + 1) : -limit;
|
||||
const auto maxId = 0;
|
||||
const auto minId = 0;
|
||||
const auto requestId = _api->request(MTPmessages_GetUnreadMentions(
|
||||
history->peer->input,
|
||||
MTP_int(offsetId),
|
||||
MTP_int(addOffset),
|
||||
MTP_int(limit),
|
||||
MTP_int(maxId),
|
||||
MTP_int(minId)
|
||||
)).done([=](const MTPmessages_Messages &result) {
|
||||
_mentionsRequests.remove(history);
|
||||
history->unreadMentions().addSlice(result, loaded);
|
||||
}).fail([=] {
|
||||
_mentionsRequests.remove(history);
|
||||
}).send();
|
||||
_mentionsRequests.emplace(history, requestId);
|
||||
}
|
||||
|
||||
void UnreadThings::requestReactions(not_null<History*> history, int loaded) {
|
||||
if (_reactionsRequests.contains(history)) {
|
||||
return;
|
||||
}
|
||||
const auto offsetId = loaded
|
||||
? std::max(history->unreadReactions().maxLoaded(), MsgId(1))
|
||||
: MsgId(1);
|
||||
const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit;
|
||||
const auto addOffset = loaded ? -(limit + 1) : -limit;
|
||||
const auto maxId = 0;
|
||||
const auto minId = 0;
|
||||
const auto requestId = _api->request(MTPmessages_GetUnreadReactions(
|
||||
history->peer->input,
|
||||
MTP_int(offsetId),
|
||||
MTP_int(addOffset),
|
||||
MTP_int(limit),
|
||||
MTP_int(maxId),
|
||||
MTP_int(minId)
|
||||
)).done([=](const MTPmessages_Messages &result) {
|
||||
_reactionsRequests.remove(history);
|
||||
history->unreadReactions().addSlice(result, loaded);
|
||||
}).fail([=] {
|
||||
_reactionsRequests.remove(history);
|
||||
}).send();
|
||||
_reactionsRequests.emplace(history, requestId);
|
||||
}
|
||||
|
||||
} // namespace UnreadThings
|
||||
44
Telegram/SourceFiles/api/api_unread_things.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class History;
|
||||
class ApiWrap;
|
||||
class PeerData;
|
||||
class ChannelData;
|
||||
|
||||
namespace Api {
|
||||
|
||||
class UnreadThings final {
|
||||
public:
|
||||
explicit UnreadThings(not_null<ApiWrap*> api);
|
||||
|
||||
[[nodiscard]] bool trackMentions(PeerData *peer) const;
|
||||
[[nodiscard]] bool trackReactions(PeerData *peer) const;
|
||||
|
||||
void preloadEnough(History *history);
|
||||
|
||||
void mediaAndMentionsRead(
|
||||
const base::flat_set<MsgId> &readIds,
|
||||
ChannelData *channel = nullptr);
|
||||
|
||||
private:
|
||||
void preloadEnoughMentions(not_null<History*> history);
|
||||
void preloadEnoughReactions(not_null<History*> history);
|
||||
|
||||
void requestMentions(not_null<History*> history, int loaded);
|
||||
void requestReactions(not_null<History*> history, int loaded);
|
||||
|
||||
const not_null<ApiWrap*> _api;
|
||||
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _mentionsRequests;
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _reactionsRequests;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Api
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_chat_participants.h"
|
||||
#include "api/api_text_entities.h"
|
||||
#include "api/api_user_privacy.h"
|
||||
#include "api/api_unread_things.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_account.h"
|
||||
#include "mtproto/mtp_instance.h"
|
||||
@@ -30,10 +31,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_scheduled_messages.h"
|
||||
#include "data/data_send_action.h"
|
||||
#include "data/data_message_reactions.h"
|
||||
#include "chat_helpers/emoji_interactions.h"
|
||||
#include "lang/lang_cloud_manager.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "core/application.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "storage/storage_facade.h"
|
||||
@@ -46,7 +49,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "apiwrap.h"
|
||||
#include "ui/text/format_values.h" // Ui::FormatPhone
|
||||
#include "app.h" // App::quitting
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
@@ -912,7 +914,7 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
|
||||
|
||||
_lastWasOnline = isOnline;
|
||||
_lastSetOnline = ms;
|
||||
if (!App::quitting()) {
|
||||
if (!Core::Quitting()) {
|
||||
_onlineRequest = api().request(MTPaccount_UpdateStatus(
|
||||
MTP_bool(!isOnline)
|
||||
)).send();
|
||||
@@ -1179,25 +1181,26 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updateReadMessagesContents: {
|
||||
const auto &d = update.c_updateReadMessagesContents();
|
||||
auto possiblyReadMentions = base::flat_set<MsgId>();
|
||||
auto unknownReadIds = base::flat_set<MsgId>();
|
||||
for (const auto &msgId : d.vmessages().v) {
|
||||
if (const auto item = _session->data().nonChannelMessage(msgId.v)) {
|
||||
if (item->isUnreadMedia() || item->isUnreadMention()) {
|
||||
item->markMediaRead();
|
||||
item->markMediaAndMentionRead();
|
||||
_session->data().requestItemRepaint(item);
|
||||
|
||||
if (item->out()
|
||||
&& item->history()->peer->isUser()
|
||||
&& !requestingDifference()) {
|
||||
item->history()->peer->asUser()->madeAction(base::unixtime::now());
|
||||
item->history()->peer->asUser()->madeAction(
|
||||
base::unixtime::now());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Perhaps it was an unread mention!
|
||||
possiblyReadMentions.insert(msgId.v);
|
||||
unknownReadIds.insert(msgId.v);
|
||||
}
|
||||
}
|
||||
session().api().checkForUnreadMentions(possiblyReadMentions);
|
||||
session().api().unreadThings().mediaAndMentionsRead(unknownReadIds);
|
||||
} break;
|
||||
|
||||
case mtpc_updateReadHistoryInbox: {
|
||||
@@ -1566,19 +1569,21 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto possiblyReadMentions = base::flat_set<MsgId>();
|
||||
auto unknownReadIds = base::flat_set<MsgId>();
|
||||
for (const auto &msgId : d.vmessages().v) {
|
||||
if (auto item = session().data().message(channel->id, msgId.v)) {
|
||||
if (item->isUnreadMedia() || item->isUnreadMention()) {
|
||||
item->markMediaRead();
|
||||
item->markMediaAndMentionRead();
|
||||
session().data().requestItemRepaint(item);
|
||||
}
|
||||
} else {
|
||||
// Perhaps it was an unread mention!
|
||||
possiblyReadMentions.insert(msgId.v);
|
||||
unknownReadIds.insert(msgId.v);
|
||||
}
|
||||
}
|
||||
session().api().checkForUnreadMentions(possiblyReadMentions, channel);
|
||||
session().api().unreadThings().mediaAndMentionsRead(
|
||||
unknownReadIds,
|
||||
channel);
|
||||
} break;
|
||||
|
||||
// Edited messages.
|
||||
@@ -1626,6 +1631,16 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
d.vmsg_id().v);
|
||||
if (item) {
|
||||
item->updateReactions(&d.vreactions());
|
||||
} else {
|
||||
const auto hasUnreadReaction = Data::Reactions::HasUnread(
|
||||
d.vreactions());
|
||||
if (hasUnreadReaction || history->unreadReactions().has()) {
|
||||
// The unread reactions count could change.
|
||||
history->owner().histories().requestDialogEntry(history);
|
||||
}
|
||||
if (hasUnreadReaction) {
|
||||
history->unreadReactions().checkAdd(d.vmsg_id().v);
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -31,34 +31,60 @@ namespace {
|
||||
|
||||
constexpr auto kContextReactionsLimit = 50;
|
||||
|
||||
struct Peers {
|
||||
std::vector<PeerId> list;
|
||||
bool unknown = false;
|
||||
};
|
||||
inline bool operator==(const Peers &a, const Peers &b) noexcept {
|
||||
return (a.list == b.list) && (a.unknown == b.unknown);
|
||||
}
|
||||
|
||||
struct PeerWithReaction {
|
||||
PeerId peer = 0;
|
||||
QString reaction;
|
||||
};
|
||||
bool operator==(const PeerWithReaction &a, const PeerWithReaction &b) {
|
||||
inline bool operator==(
|
||||
const PeerWithReaction &a,
|
||||
const PeerWithReaction &b) noexcept {
|
||||
return (a.peer == b.peer) && (a.reaction == b.reaction);
|
||||
}
|
||||
|
||||
struct PeersWithReactions {
|
||||
std::vector<PeerWithReaction> list;
|
||||
std::vector<PeerId> read;
|
||||
int fullReactionsCount = 0;
|
||||
bool unknown = false;
|
||||
};
|
||||
inline bool operator==(
|
||||
const PeersWithReactions &a,
|
||||
const PeersWithReactions &b) noexcept {
|
||||
return (a.fullReactionsCount == b.fullReactionsCount)
|
||||
&& (a.list == b.list)
|
||||
&& (a.read == b.read)
|
||||
&& (a.unknown == b.unknown);
|
||||
}
|
||||
|
||||
struct CachedRead {
|
||||
explicit CachedRead(PeerId unknownFlag)
|
||||
: list(std::vector<PeerId>{ unknownFlag }) {
|
||||
CachedRead()
|
||||
: data(Peers{ .unknown = true }) {
|
||||
}
|
||||
rpl::variable<std::vector<PeerId>> list;
|
||||
rpl::variable<Peers> data;
|
||||
mtpRequestId requestId = 0;
|
||||
};
|
||||
|
||||
struct CachedReacted {
|
||||
explicit CachedReacted(PeerId unknownFlag)
|
||||
: list(
|
||||
std::vector<PeerWithReaction>{ PeerWithReaction{ unknownFlag } }) {
|
||||
CachedReacted()
|
||||
: data(PeersWithReactions{ .unknown = true }) {
|
||||
}
|
||||
rpl::variable<std::vector<PeerWithReaction>> list;
|
||||
rpl::variable<PeersWithReactions> data;
|
||||
mtpRequestId requestId = 0;
|
||||
};
|
||||
|
||||
struct Context {
|
||||
base::flat_map<not_null<HistoryItem*>, CachedRead> cachedRead;
|
||||
base::flat_map<not_null<HistoryItem*>, CachedReacted> cachedReacted;
|
||||
base::flat_map<
|
||||
not_null<HistoryItem*>,
|
||||
base::flat_map<QString, CachedReacted>> cachedReacted;
|
||||
base::flat_map<not_null<Main::Session*>, rpl::lifetime> subscriptions;
|
||||
|
||||
[[nodiscard]] CachedRead &cacheRead(not_null<HistoryItem*> item) {
|
||||
@@ -66,21 +92,18 @@ struct Context {
|
||||
if (i != end(cachedRead)) {
|
||||
return i->second;
|
||||
}
|
||||
return cachedRead.emplace(
|
||||
item,
|
||||
CachedRead(item->history()->session().userPeerId())
|
||||
).first->second;
|
||||
return cachedRead.emplace(item, CachedRead()).first->second;
|
||||
}
|
||||
|
||||
[[nodiscard]] CachedReacted &cacheReacted(not_null<HistoryItem*> item) {
|
||||
const auto i = cachedReacted.find(item);
|
||||
if (i != end(cachedReacted)) {
|
||||
[[nodiscard]] CachedReacted &cacheReacted(
|
||||
not_null<HistoryItem*> item,
|
||||
const QString &reaction) {
|
||||
auto &map = cachedReacted[item];
|
||||
const auto i = map.find(reaction);
|
||||
if (i != end(map)) {
|
||||
return i->second;
|
||||
}
|
||||
return cachedReacted.emplace(
|
||||
item,
|
||||
CachedReacted(item->history()->session().userPeerId())
|
||||
).first->second;
|
||||
return map.emplace(reaction, CachedReacted()).first->second;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -124,9 +147,11 @@ struct State {
|
||||
item->history()->session().api().request(requestId).cancel();
|
||||
}
|
||||
}
|
||||
for (auto &[item, entry] : i->second->cachedReacted) {
|
||||
if (const auto requestId = entry.requestId) {
|
||||
item->history()->session().api().request(requestId).cancel();
|
||||
for (auto &[item, map] : i->second->cachedReacted) {
|
||||
for (auto &[reaction, entry] : map) {
|
||||
if (const auto requestId = entry.requestId) {
|
||||
item->history()->session().api().request(requestId).cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
contexts.erase(i);
|
||||
@@ -134,7 +159,9 @@ struct State {
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] not_null<Context*> PreparedContextAt(not_null<QWidget*> key, not_null<Main::Session*> session) {
|
||||
[[nodiscard]] not_null<Context*> PreparedContextAt(
|
||||
not_null<QWidget*> key,
|
||||
not_null<Main::Session*> session) {
|
||||
const auto context = ContextAt(key);
|
||||
if (context->subscriptions.contains(session)) {
|
||||
return context;
|
||||
@@ -149,7 +176,9 @@ struct State {
|
||||
}
|
||||
const auto j = context->cachedReacted.find(update.item);
|
||||
if (j != end(context->cachedReacted)) {
|
||||
session->api().request(j->second.requestId).cancel();
|
||||
for (auto &[reaction, entry] : j->second) {
|
||||
session->api().request(entry.requestId).cancel();
|
||||
}
|
||||
context->cachedReacted.erase(j);
|
||||
}
|
||||
}, context->subscriptions[session]);
|
||||
@@ -163,21 +192,6 @@ struct State {
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ListUnknown(
|
||||
const std::vector<PeerId> &list,
|
||||
not_null<HistoryItem*> item) {
|
||||
return (list.size() == 1)
|
||||
&& (list.front() == item->history()->session().userPeerId());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ListUnknown(
|
||||
const std::vector<PeerWithReaction> &list,
|
||||
not_null<HistoryItem*> item) {
|
||||
return (list.size() == 1)
|
||||
&& list.front().reaction.isEmpty()
|
||||
&& (list.front().peer == item->history()->session().userPeerId());
|
||||
}
|
||||
|
||||
[[nodiscard]] Ui::WhoReadType DetectSeenType(not_null<HistoryItem*> item) {
|
||||
if (const auto media = item->media()) {
|
||||
if (!media->webpage()) {
|
||||
@@ -193,7 +207,7 @@ struct State {
|
||||
return Ui::WhoReadType::Seen;
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<std::vector<PeerId>> WhoReadIds(
|
||||
[[nodiscard]] rpl::producer<Peers> WhoReadIds(
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<QWidget*> context) {
|
||||
auto weak = QPointer<QWidget>(context.get());
|
||||
@@ -213,33 +227,39 @@ struct State {
|
||||
).done([=](const MTPVector<MTPlong> &result) {
|
||||
auto &entry = context->cacheRead(item);
|
||||
entry.requestId = 0;
|
||||
auto peers = std::vector<PeerId>();
|
||||
peers.reserve(std::max(int(result.v.size()), 1));
|
||||
auto parsed = Peers();
|
||||
parsed.list.reserve(result.v.size());
|
||||
for (const auto &id : result.v) {
|
||||
peers.push_back(UserId(id));
|
||||
parsed.list.push_back(UserId(id));
|
||||
}
|
||||
entry.list = std::move(peers);
|
||||
entry.data = std::move(parsed);
|
||||
}).fail([=] {
|
||||
auto &entry = context->cacheRead(item);
|
||||
entry.requestId = 0;
|
||||
if (ListUnknown(entry.list.current(), item)) {
|
||||
entry.list = std::vector<PeerId>();
|
||||
if (entry.data.current().unknown) {
|
||||
entry.data = Peers();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
return entry.list.value().start_existing(consumer);
|
||||
return entry.data.value().start_existing(consumer);
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector < PeerWithReaction> WithEmptyReactions(
|
||||
const std::vector<PeerId> &peers) {
|
||||
return peers | ranges::views::transform([](PeerId peer) {
|
||||
return PeerWithReaction{ .peer = peer };
|
||||
}) | ranges::to_vector;
|
||||
[[nodiscard]] PeersWithReactions WithEmptyReactions(
|
||||
Peers &&peers) {
|
||||
auto result = PeersWithReactions{
|
||||
.list = peers.list | ranges::views::transform([](PeerId peer) {
|
||||
return PeerWithReaction{.peer = peer };
|
||||
}) | ranges::to_vector,
|
||||
.unknown = peers.unknown,
|
||||
};
|
||||
result.read = std::move(peers.list);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<std::vector<PeerWithReaction>> WhoReactedIds(
|
||||
[[nodiscard]] rpl::producer<PeersWithReactions> WhoReactedIds(
|
||||
not_null<HistoryItem*> item,
|
||||
const QString &reaction,
|
||||
not_null<QWidget*> context) {
|
||||
auto weak = QPointer<QWidget>(context.get());
|
||||
const auto session = &item->history()->session();
|
||||
@@ -248,68 +268,74 @@ struct State {
|
||||
return rpl::lifetime();
|
||||
}
|
||||
const auto context = PreparedContextAt(weak.data(), session);
|
||||
auto &entry = context->cacheReacted(item);
|
||||
auto &entry = context->cacheReacted(item, reaction);
|
||||
if (!entry.requestId) {
|
||||
using Flag = MTPmessages_GetMessageReactionsList::Flag;
|
||||
entry.requestId = session->api().request(
|
||||
MTPmessages_GetMessageReactionsList(
|
||||
MTP_flags(0),
|
||||
MTP_flags(reaction.isEmpty()
|
||||
? Flag(0)
|
||||
: Flag::f_reaction),
|
||||
item->history()->peer->input,
|
||||
MTP_int(item->id),
|
||||
MTPstring(), // reaction
|
||||
MTP_string(reaction),
|
||||
MTPstring(), // offset
|
||||
MTP_int(kContextReactionsLimit)
|
||||
)
|
||||
).done([=](const MTPmessages_MessageReactionsList &result) {
|
||||
auto &entry = context->cacheReacted(item);
|
||||
auto &entry = context->cacheReacted(item, reaction);
|
||||
entry.requestId = 0;
|
||||
|
||||
result.match([&](
|
||||
const MTPDmessages_messageReactionsList &data) {
|
||||
session->data().processUsers(data.vusers());
|
||||
session->data().processChats(data.vchats());
|
||||
|
||||
auto peers = std::vector<PeerWithReaction>();
|
||||
peers.reserve(data.vreactions().v.size());
|
||||
auto parsed = PeersWithReactions{
|
||||
.fullReactionsCount = data.vcount().v,
|
||||
};
|
||||
parsed.list.reserve(data.vreactions().v.size());
|
||||
for (const auto &vote : data.vreactions().v) {
|
||||
vote.match([&](const auto &data) {
|
||||
peers.push_back(PeerWithReaction{
|
||||
.peer = peerFromUser(data.vuser_id()),
|
||||
parsed.list.push_back(PeerWithReaction{
|
||||
.peer = peerFromMTP(data.vpeer_id()),
|
||||
.reaction = qs(data.vreaction()),
|
||||
});
|
||||
});
|
||||
}
|
||||
entry.list = std::move(peers);
|
||||
entry.data = std::move(parsed);
|
||||
});
|
||||
}).fail([=] {
|
||||
auto &entry = context->cacheReacted(item);
|
||||
auto &entry = context->cacheReacted(item, reaction);
|
||||
entry.requestId = 0;
|
||||
if (ListUnknown(entry.list.current(), item)) {
|
||||
entry.list = std::vector<PeerWithReaction>();
|
||||
if (entry.data.current().unknown) {
|
||||
entry.data = PeersWithReactions();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
return entry.list.value().start_existing(consumer);
|
||||
return entry.data.value().start_existing(consumer);
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] auto WhoReadOrReactedIds(
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<QWidget*> context)
|
||||
-> rpl::producer<std::vector<PeerWithReaction>> {
|
||||
-> rpl::producer<PeersWithReactions> {
|
||||
return rpl::combine(
|
||||
WhoReactedIds(item, context),
|
||||
WhoReactedIds(item, QString(), context),
|
||||
WhoReadIds(item, context)
|
||||
) | rpl::map([=](
|
||||
std::vector<PeerWithReaction> reacted,
|
||||
std::vector<PeerId> read) {
|
||||
if (ListUnknown(reacted, item) || ListUnknown(read, item)) {
|
||||
return reacted;
|
||||
) | rpl::map([=](PeersWithReactions &&reacted, Peers &&read) {
|
||||
if (reacted.unknown || read.unknown) {
|
||||
return PeersWithReactions{ .unknown = true };
|
||||
}
|
||||
for (const auto &peer : read) {
|
||||
if (!ranges::contains(reacted, peer, &PeerWithReaction::peer)) {
|
||||
reacted.push_back({ .peer = peer });
|
||||
auto &list = reacted.list;
|
||||
for (const auto &peer : read.list) {
|
||||
if (!ranges::contains(list, peer, &PeerWithReaction::peer)) {
|
||||
list.push_back({ .peer = peer });
|
||||
}
|
||||
}
|
||||
return reacted;
|
||||
reacted.read = std::move(read.list);
|
||||
return std::move(reacted);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -418,6 +444,108 @@ void RegenerateParticipants(not_null<State*> state, int small, int large) {
|
||||
RegenerateUserpics(state, small, large);
|
||||
}
|
||||
|
||||
rpl::producer<Ui::WhoReadContent> WhoReacted(
|
||||
not_null<HistoryItem*> item,
|
||||
const QString &reaction,
|
||||
not_null<QWidget*> context,
|
||||
const style::WhoRead &st,
|
||||
std::shared_ptr<WhoReadList> whoReadIds) {
|
||||
const auto small = st.userpics.size;
|
||||
const auto large = st.photoSize;
|
||||
return [=](auto consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
|
||||
const auto resolveWhoRead = reaction.isEmpty()
|
||||
&& WhoReadExists(item);
|
||||
|
||||
const auto state = lifetime.make_state<State>();
|
||||
const auto pushNext = [=] {
|
||||
consumer.put_next_copy(state->current);
|
||||
};
|
||||
|
||||
const auto resolveWhoReacted = !reaction.isEmpty()
|
||||
|| item->canViewReactions();
|
||||
auto idsWithReactions = (resolveWhoRead && resolveWhoReacted)
|
||||
? WhoReadOrReactedIds(item, context)
|
||||
: resolveWhoRead
|
||||
? (WhoReadIds(item, context) | rpl::map(WithEmptyReactions))
|
||||
: WhoReactedIds(item, reaction, context);
|
||||
state->current.type = resolveWhoRead
|
||||
? DetectSeenType(item)
|
||||
: Ui::WhoReadType::Reacted;
|
||||
if (resolveWhoReacted) {
|
||||
const auto &list = item->reactions();
|
||||
state->current.fullReactionsCount = reaction.isEmpty()
|
||||
? ranges::accumulate(
|
||||
list,
|
||||
0,
|
||||
ranges::plus{},
|
||||
[](const auto &pair) { return pair.second; })
|
||||
: list.contains(reaction)
|
||||
? list.find(reaction)->second
|
||||
: 0;
|
||||
|
||||
// #TODO reactions
|
||||
state->current.singleReaction = !reaction.isEmpty()
|
||||
? reaction
|
||||
: (list.size() == 1)
|
||||
? list.front().first
|
||||
: QString();
|
||||
}
|
||||
std::move(
|
||||
idsWithReactions
|
||||
) | rpl::start_with_next([=](PeersWithReactions &&peers) {
|
||||
if (peers.unknown) {
|
||||
state->userpics.clear();
|
||||
consumer.put_next(Ui::WhoReadContent{
|
||||
.type = state->current.type,
|
||||
.fullReactionsCount = state->current.fullReactionsCount,
|
||||
.fullReadCount = state->current.fullReadCount,
|
||||
.unknown = true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
state->current.fullReadCount = int(peers.read.size());
|
||||
state->current.fullReactionsCount = peers.fullReactionsCount;
|
||||
if (whoReadIds) {
|
||||
const auto reacted = peers.list.size() - ranges::count(
|
||||
peers.list,
|
||||
QString(),
|
||||
&PeerWithReaction::reaction);
|
||||
whoReadIds->list = (peers.read.size() > reacted)
|
||||
? std::move(peers.read)
|
||||
: std::vector<PeerId>();
|
||||
}
|
||||
if (UpdateUserpics(state, item, peers.list)) {
|
||||
RegenerateParticipants(state, small, large);
|
||||
pushNext();
|
||||
} else if (peers.list.empty()) {
|
||||
pushNext();
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
item->history()->session().downloaderTaskFinished(
|
||||
) | rpl::filter([=] {
|
||||
return state->someUserpicsNotLoaded && !state->scheduled;
|
||||
}) | rpl::start_with_next([=] {
|
||||
for (const auto &userpic : state->userpics) {
|
||||
if (userpic.peer->userpicUniqueKey(userpic.view)
|
||||
!= userpic.uniqueKey) {
|
||||
state->scheduled = true;
|
||||
crl::on_main(&state->guard, [=] {
|
||||
state->scheduled = false;
|
||||
RegenerateUserpics(state, small, large);
|
||||
pushNext();
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
return lifetime;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool WhoReadExists(not_null<HistoryItem*> item) {
|
||||
@@ -462,78 +590,17 @@ bool WhoReactedExists(not_null<HistoryItem*> item) {
|
||||
rpl::producer<Ui::WhoReadContent> WhoReacted(
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<QWidget*> context,
|
||||
const style::WhoRead &st,
|
||||
std::shared_ptr<WhoReadList> whoReadIds) {
|
||||
return WhoReacted(item, QString(), context, st, std::move(whoReadIds));
|
||||
}
|
||||
|
||||
rpl::producer<Ui::WhoReadContent> WhoReacted(
|
||||
not_null<HistoryItem*> item,
|
||||
const QString &reaction,
|
||||
not_null<QWidget*> context,
|
||||
const style::WhoRead &st) {
|
||||
const auto small = st.userpics.size;
|
||||
const auto large = st.photoSize;
|
||||
return [=](auto consumer) {
|
||||
auto lifetime = rpl::lifetime();
|
||||
|
||||
const auto resolveWhoRead = WhoReadExists(item);
|
||||
|
||||
const auto state = lifetime.make_state<State>();
|
||||
const auto pushNext = [=] {
|
||||
consumer.put_next_copy(state->current);
|
||||
};
|
||||
|
||||
const auto resolveWhoReacted = item->canViewReactions();
|
||||
auto idsWithReactions = (resolveWhoRead && resolveWhoReacted)
|
||||
? WhoReadOrReactedIds(item, context)
|
||||
: resolveWhoRead
|
||||
? (WhoReadIds(item, context) | rpl::map(WithEmptyReactions))
|
||||
: WhoReactedIds(item, context);
|
||||
state->current.type = resolveWhoRead
|
||||
? DetectSeenType(item)
|
||||
: Ui::WhoReadType::Reacted;
|
||||
if (resolveWhoReacted) {
|
||||
const auto &list = item->reactions();
|
||||
state->current.fullReactionsCount = ranges::accumulate(
|
||||
list,
|
||||
0,
|
||||
ranges::plus{},
|
||||
[](const auto &pair) { return pair.second; });
|
||||
|
||||
// #TODO reactions
|
||||
state->current.mostPopularReaction = item->reactions().front().first;
|
||||
}
|
||||
std::move(
|
||||
idsWithReactions
|
||||
) | rpl::start_with_next([=](
|
||||
const std::vector<PeerWithReaction> &peers) {
|
||||
if (ListUnknown(peers, item)) {
|
||||
state->userpics.clear();
|
||||
consumer.put_next(Ui::WhoReadContent{
|
||||
.type = state->current.type,
|
||||
.unknown = true,
|
||||
});
|
||||
return;
|
||||
} else if (UpdateUserpics(state, item, peers)) {
|
||||
RegenerateParticipants(state, small, large);
|
||||
pushNext();
|
||||
} else if (peers.empty()) {
|
||||
pushNext();
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
item->history()->session().downloaderTaskFinished(
|
||||
) | rpl::filter([=] {
|
||||
return state->someUserpicsNotLoaded && !state->scheduled;
|
||||
}) | rpl::start_with_next([=] {
|
||||
for (const auto &userpic : state->userpics) {
|
||||
if (userpic.peer->userpicUniqueKey(userpic.view)
|
||||
!= userpic.uniqueKey) {
|
||||
state->scheduled = true;
|
||||
crl::on_main(&state->guard, [=] {
|
||||
state->scheduled = false;
|
||||
RegenerateUserpics(state, small, large);
|
||||
pushNext();
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, lifetime);
|
||||
|
||||
return lifetime;
|
||||
};
|
||||
return WhoReacted(item, reaction, context, st, nullptr);
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -15,6 +15,7 @@ struct WhoRead;
|
||||
|
||||
namespace Ui {
|
||||
struct WhoReadContent;
|
||||
enum class WhoReadType;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Api {
|
||||
@@ -22,10 +23,21 @@ namespace Api {
|
||||
[[nodiscard]] bool WhoReadExists(not_null<HistoryItem*> item);
|
||||
[[nodiscard]] bool WhoReactedExists(not_null<HistoryItem*> item);
|
||||
|
||||
struct WhoReadList {
|
||||
std::vector<PeerId> list;
|
||||
Ui::WhoReadType type = {};
|
||||
};
|
||||
|
||||
// The context must be destroyed before the session holding this item.
|
||||
[[nodiscard]] rpl::producer<Ui::WhoReadContent> WhoReacted(
|
||||
not_null<HistoryItem*> item,
|
||||
not_null<QWidget*> context,
|
||||
const style::WhoRead &st); // Cache results for this lifetime.
|
||||
not_null<QWidget*> context, // Cache results for this lifetime.
|
||||
const style::WhoRead &st,
|
||||
std::shared_ptr<WhoReadList> whoReadIds = nullptr);
|
||||
[[nodiscard]] rpl::producer<Ui::WhoReadContent> WhoReacted(
|
||||
not_null<HistoryItem*> item,
|
||||
const QString &reaction,
|
||||
not_null<QWidget*> context, // Cache results for this lifetime.
|
||||
const style::WhoRead &st);
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_user_privacy.h"
|
||||
#include "api/api_views.h"
|
||||
#include "api/api_confirm_phone.h"
|
||||
#include "api/api_unread_things.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_changes.h"
|
||||
@@ -51,7 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/application.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "base/random.h"
|
||||
#include "base/qt_adapters.h"
|
||||
#include "base/qt/qt_common_adapters.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwindow.h"
|
||||
@@ -73,6 +74,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "inline_bots/inline_bot_result.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "ui/item_text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/emoji_config.h"
|
||||
#include "ui/chat/attach/attach_prepare.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
@@ -86,7 +88,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "facades.h"
|
||||
#include "app.h" // App::quitting
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -96,9 +97,6 @@ constexpr auto kSaveCloudDraftTimeout = 1000;
|
||||
constexpr auto kTopPromotionInterval = TimeId(60 * 60);
|
||||
constexpr auto kTopPromotionMinDelay = TimeId(10);
|
||||
constexpr auto kSmallDelayMs = 5;
|
||||
constexpr auto kUnreadMentionsPreloadIfLess = 5;
|
||||
constexpr auto kUnreadMentionsFirstRequestLimit = 10;
|
||||
constexpr auto kUnreadMentionsNextRequestLimit = 100;
|
||||
constexpr auto kSharedMediaLimit = 100;
|
||||
constexpr auto kReadFeaturedSetsTimeout = crl::time(1000);
|
||||
constexpr auto kFileLoaderQueueStopTimeout = crl::time(5000);
|
||||
@@ -142,7 +140,8 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||
, _confirmPhone(std::make_unique<Api::ConfirmPhone>(this))
|
||||
, _peerPhoto(std::make_unique<Api::PeerPhoto>(this))
|
||||
, _polls(std::make_unique<Api::Polls>(this))
|
||||
, _chatParticipants(std::make_unique<Api::ChatParticipants>(this)) {
|
||||
, _chatParticipants(std::make_unique<Api::ChatParticipants>(this))
|
||||
, _unreadThings(std::make_unique<Api::UnreadThings>(this)) {
|
||||
crl::on_main(session, [=] {
|
||||
// You can't use _session->lifetime() in the constructor,
|
||||
// only queued, because it is not constructed yet.
|
||||
@@ -470,13 +469,14 @@ void ApiWrap::sendMessageFail(
|
||||
Ui::show(Box<Ui::InformBox>(
|
||||
PeerFloodErrorText(&session(), PeerFloodType::Send)));
|
||||
} else if (error.type() == qstr("USER_BANNED_IN_CHANNEL")) {
|
||||
const auto link = textcmdLink(
|
||||
session().createInternalLinkFull(qsl("spambot")),
|
||||
tr::lng_cant_more_info(tr::now));
|
||||
const auto link = Ui::Text::Link(
|
||||
tr::lng_cant_more_info(tr::now),
|
||||
session().createInternalLinkFull(qsl("spambot")));
|
||||
Ui::show(Box<Ui::InformBox>(tr::lng_error_public_groups_denied(
|
||||
tr::now,
|
||||
lt_more_info,
|
||||
link)));
|
||||
link,
|
||||
Ui::Text::WithEntities)));
|
||||
} else if (error.type().startsWith(qstr("SLOWMODE_WAIT_"))) {
|
||||
const auto chop = qstr("SLOWMODE_WAIT_").size();
|
||||
const auto left = base::StringViewMid(error.type(), chop).toInt();
|
||||
@@ -1286,7 +1286,7 @@ void ApiWrap::migrateFail(not_null<PeerData*> peer, const QString &error) {
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::markMediaRead(
|
||||
void ApiWrap::markContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items) {
|
||||
auto markedIds = QVector<MTPint>();
|
||||
auto channelMarkedIds = base::flat_map<
|
||||
@@ -1294,12 +1294,7 @@ void ApiWrap::markMediaRead(
|
||||
QVector<MTPint>>();
|
||||
markedIds.reserve(items.size());
|
||||
for (const auto &item : items) {
|
||||
if ((!item->isUnreadMedia() || item->out())
|
||||
&& !item->isUnreadMention()) {
|
||||
continue;
|
||||
}
|
||||
item->markMediaRead();
|
||||
if (!item->isRegular()) {
|
||||
if (!item->markContentsRead(true) || !item->isRegular()) {
|
||||
continue;
|
||||
}
|
||||
if (const auto channel = item->history()->peer->asChannel()) {
|
||||
@@ -1323,13 +1318,8 @@ void ApiWrap::markMediaRead(
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::markMediaRead(not_null<HistoryItem*> item) {
|
||||
if ((!item->isUnreadMedia() || item->out())
|
||||
&& !item->isUnreadMention()) {
|
||||
return;
|
||||
}
|
||||
item->markMediaRead();
|
||||
if (!item->isRegular()) {
|
||||
void ApiWrap::markContentsRead(not_null<HistoryItem*> item) {
|
||||
if (!item->markContentsRead(true) || !item->isRegular()) {
|
||||
return;
|
||||
}
|
||||
const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id));
|
||||
@@ -2125,7 +2115,7 @@ bool ApiWrap::isQuitPrevent() {
|
||||
|
||||
void ApiWrap::checkQuitPreventFinished() {
|
||||
if (_draftsSaveRequestIds.empty()) {
|
||||
if (App::quitting()) {
|
||||
if (Core::Quitting()) {
|
||||
LOG(("ApiWrap doesn't prevent quit any more."));
|
||||
}
|
||||
Core::App().quitPreventFinished();
|
||||
@@ -2909,45 +2899,6 @@ void ApiWrap::jumpToHistoryDate(not_null<PeerData*> peer, const QDate &date) {
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::preloadEnoughUnreadMentions(not_null<History*> history) {
|
||||
auto fullCount = history->getUnreadMentionsCount();
|
||||
auto loadedCount = history->getUnreadMentionsLoadedCount();
|
||||
auto allLoaded = (fullCount >= 0) ? (loadedCount >= fullCount) : false;
|
||||
if (fullCount < 0 || loadedCount >= kUnreadMentionsPreloadIfLess || allLoaded) {
|
||||
return;
|
||||
}
|
||||
if (_unreadMentionsRequests.contains(history)) {
|
||||
return;
|
||||
}
|
||||
auto offsetId = loadedCount ? history->getMaxLoadedUnreadMention() : 1;
|
||||
auto limit = loadedCount ? kUnreadMentionsNextRequestLimit : kUnreadMentionsFirstRequestLimit;
|
||||
auto addOffset = loadedCount ? -(limit + 1) : -limit;
|
||||
auto maxId = 0;
|
||||
auto minId = 0;
|
||||
auto requestId = request(MTPmessages_GetUnreadMentions(history->peer->input, MTP_int(offsetId), MTP_int(addOffset), MTP_int(limit), MTP_int(maxId), MTP_int(minId))).done([this, history](const MTPmessages_Messages &result) {
|
||||
_unreadMentionsRequests.remove(history);
|
||||
history->addUnreadMentionsSlice(result);
|
||||
}).fail([this, history] {
|
||||
_unreadMentionsRequests.remove(history);
|
||||
}).send();
|
||||
_unreadMentionsRequests.emplace(history, requestId);
|
||||
}
|
||||
|
||||
void ApiWrap::checkForUnreadMentions(
|
||||
const base::flat_set<MsgId> &possiblyReadMentions,
|
||||
ChannelData *channel) {
|
||||
for (const auto &msgId : possiblyReadMentions) {
|
||||
requestMessageData(channel, msgId, [=] {
|
||||
const auto item = channel
|
||||
? _session->data().message(channel->id, msgId)
|
||||
: _session->data().nonChannelMessage(msgId);
|
||||
if (item && item->mentionsMe()) {
|
||||
item->markMediaRead();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::requestSharedMediaCount(
|
||||
not_null<PeerData*> peer,
|
||||
Storage::SharedMediaType type) {
|
||||
@@ -4145,3 +4096,7 @@ Api::Polls &ApiWrap::polls() {
|
||||
Api::ChatParticipants &ApiWrap::chatParticipants() {
|
||||
return *_chatParticipants;
|
||||
}
|
||||
|
||||
Api::UnreadThings &ApiWrap::unreadThings() {
|
||||
return *_unreadThings;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ class ConfirmPhone;
|
||||
class PeerPhoto;
|
||||
class Polls;
|
||||
class ChatParticipants;
|
||||
class UnreadThings;
|
||||
|
||||
namespace details {
|
||||
|
||||
@@ -206,8 +207,9 @@ public:
|
||||
FnMut<void(not_null<ChannelData*>)> done,
|
||||
Fn<void(const QString &)> fail = nullptr);
|
||||
|
||||
void markMediaRead(const base::flat_set<not_null<HistoryItem*>> &items);
|
||||
void markMediaRead(not_null<HistoryItem*> item);
|
||||
void markContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items);
|
||||
void markContentsRead(not_null<HistoryItem*> item);
|
||||
|
||||
void deleteAllFromParticipant(
|
||||
not_null<ChannelData*> channel,
|
||||
@@ -250,11 +252,6 @@ public:
|
||||
|
||||
void jumpToDate(Dialogs::Key chat, const QDate &date);
|
||||
|
||||
void preloadEnoughUnreadMentions(not_null<History*> history);
|
||||
void checkForUnreadMentions(
|
||||
const base::flat_set<MsgId> &possiblyReadMentions,
|
||||
ChannelData *channel = nullptr);
|
||||
|
||||
using SliceType = Data::LoadDirection;
|
||||
void requestSharedMedia(
|
||||
not_null<PeerData*> peer,
|
||||
@@ -356,6 +353,7 @@ public:
|
||||
[[nodiscard]] Api::PeerPhoto &peerPhoto();
|
||||
[[nodiscard]] Api::Polls &polls();
|
||||
[[nodiscard]] Api::ChatParticipants &chatParticipants();
|
||||
[[nodiscard]] Api::UnreadThings &unreadThings();
|
||||
|
||||
void updatePrivacyLastSeens();
|
||||
|
||||
@@ -562,8 +560,6 @@ private:
|
||||
mtpRequestId _contactsRequestId = 0;
|
||||
mtpRequestId _contactsStatusesRequestId = 0;
|
||||
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _unreadMentionsRequests;
|
||||
|
||||
base::flat_set<std::tuple<
|
||||
not_null<PeerData*>,
|
||||
SharedMediaType,
|
||||
@@ -636,6 +632,7 @@ private:
|
||||
const std::unique_ptr<Api::PeerPhoto> _peerPhoto;
|
||||
const std::unique_ptr<Api::Polls> _polls;
|
||||
const std::unique_ptr<Api::ChatParticipants> _chatParticipants;
|
||||
const std::unique_ptr<Api::UnreadThings> _unreadThings;
|
||||
|
||||
mtpRequestId _wallPaperRequestId = 0;
|
||||
QString _wallPaperSlug;
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "app.h"
|
||||
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "core/update_checker.h"
|
||||
#include "core/sandbox.h"
|
||||
#include "core/application.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtGui/QFontDatabase>
|
||||
|
||||
namespace {
|
||||
|
||||
App::LaunchState _launchState = App::Launched;
|
||||
|
||||
HistoryView::Element *hoveredItem = nullptr,
|
||||
*pressedItem = nullptr,
|
||||
*hoveredLinkItem = nullptr,
|
||||
*pressedLinkItem = nullptr,
|
||||
*mousedItem = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace App {
|
||||
|
||||
void hoveredItem(HistoryView::Element *item) {
|
||||
::hoveredItem = item;
|
||||
}
|
||||
|
||||
HistoryView::Element *hoveredItem() {
|
||||
return ::hoveredItem;
|
||||
}
|
||||
|
||||
void pressedItem(HistoryView::Element *item) {
|
||||
::pressedItem = item;
|
||||
}
|
||||
|
||||
HistoryView::Element *pressedItem() {
|
||||
return ::pressedItem;
|
||||
}
|
||||
|
||||
void hoveredLinkItem(HistoryView::Element *item) {
|
||||
::hoveredLinkItem = item;
|
||||
}
|
||||
|
||||
HistoryView::Element *hoveredLinkItem() {
|
||||
return ::hoveredLinkItem;
|
||||
}
|
||||
|
||||
void pressedLinkItem(HistoryView::Element *item) {
|
||||
::pressedLinkItem = item;
|
||||
}
|
||||
|
||||
HistoryView::Element *pressedLinkItem() {
|
||||
return ::pressedLinkItem;
|
||||
}
|
||||
|
||||
void mousedItem(HistoryView::Element *item) {
|
||||
::mousedItem = item;
|
||||
}
|
||||
|
||||
HistoryView::Element *mousedItem() {
|
||||
return ::mousedItem;
|
||||
}
|
||||
|
||||
void clearMousedItems() {
|
||||
hoveredItem(nullptr);
|
||||
pressedItem(nullptr);
|
||||
hoveredLinkItem(nullptr);
|
||||
pressedLinkItem(nullptr);
|
||||
mousedItem(nullptr);
|
||||
}
|
||||
|
||||
void quit() {
|
||||
if (quitting()) {
|
||||
return;
|
||||
} else if (Core::IsAppLaunched()
|
||||
&& Core::App().exportPreventsQuit()) {
|
||||
return;
|
||||
}
|
||||
setLaunchState(QuitRequested);
|
||||
|
||||
if (auto window = App::wnd()) {
|
||||
if (!Core::Sandbox::Instance().isSavingSession()) {
|
||||
window->hide();
|
||||
}
|
||||
}
|
||||
Core::Application::QuitAttempt();
|
||||
}
|
||||
|
||||
bool quitting() {
|
||||
return _launchState != Launched;
|
||||
}
|
||||
|
||||
LaunchState launchState() {
|
||||
return _launchState;
|
||||
}
|
||||
|
||||
void setLaunchState(LaunchState state) {
|
||||
_launchState = state;
|
||||
}
|
||||
|
||||
void restart() {
|
||||
using namespace Core;
|
||||
const auto updateReady = !UpdaterDisabled()
|
||||
&& (UpdateChecker().state() == UpdateChecker::State::Ready);
|
||||
if (updateReady) {
|
||||
cSetRestartingUpdate(true);
|
||||
} else {
|
||||
cSetRestarting(true);
|
||||
cSetRestartingToSettings(true);
|
||||
}
|
||||
App::quit();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace HistoryView {
|
||||
class Element;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace App {
|
||||
void hoveredItem(HistoryView::Element *item);
|
||||
HistoryView::Element *hoveredItem();
|
||||
void pressedItem(HistoryView::Element *item);
|
||||
HistoryView::Element *pressedItem();
|
||||
void hoveredLinkItem(HistoryView::Element *item);
|
||||
HistoryView::Element *hoveredLinkItem();
|
||||
void pressedLinkItem(HistoryView::Element *item);
|
||||
HistoryView::Element *pressedLinkItem();
|
||||
void mousedItem(HistoryView::Element *item);
|
||||
HistoryView::Element *mousedItem();
|
||||
void clearMousedItems();
|
||||
|
||||
enum LaunchState {
|
||||
Launched = 0,
|
||||
QuitRequested = 1,
|
||||
QuitProcessed = 2,
|
||||
};
|
||||
void quit();
|
||||
bool quitting();
|
||||
LaunchState launchState();
|
||||
void setLaunchState(LaunchState state);
|
||||
void restart();
|
||||
|
||||
};
|
||||
@@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/special_fields.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/unread_badge.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "data/data_channel.h"
|
||||
@@ -112,16 +113,19 @@ style::InputField CreateBioFieldStyle() {
|
||||
return result;
|
||||
}
|
||||
|
||||
QString PeerFloodErrorText(
|
||||
TextWithEntities PeerFloodErrorText(
|
||||
not_null<Main::Session*> session,
|
||||
PeerFloodType type) {
|
||||
const auto link = textcmdLink(
|
||||
session->createInternalLinkFull(qsl("spambot")),
|
||||
tr::lng_cant_more_info(tr::now));
|
||||
if (type == PeerFloodType::InviteGroup) {
|
||||
return tr::lng_cant_invite_not_contact(tr::now, lt_more_info, link);
|
||||
}
|
||||
return tr::lng_cant_send_to_not_contact(tr::now, lt_more_info, link);
|
||||
const auto link = Ui::Text::Link(
|
||||
tr::lng_cant_more_info(tr::now),
|
||||
session->createInternalLinkFull(qsl("spambot")));
|
||||
return ((type == PeerFloodType::InviteGroup)
|
||||
? tr::lng_cant_invite_not_contact
|
||||
: tr::lng_cant_send_to_not_contact)(
|
||||
tr::now,
|
||||
lt_more_info,
|
||||
link,
|
||||
Ui::Text::WithEntities);
|
||||
}
|
||||
|
||||
void ShowAddParticipantsError(
|
||||
@@ -167,6 +171,14 @@ void ShowAddParticipantsError(
|
||||
}
|
||||
}
|
||||
const auto hasBot = ranges::any_of(users, &UserData::isBot);
|
||||
if (error == u"PEER_FLOOD"_q) {
|
||||
const auto type = (chat->isChat() || chat->isMegagroup())
|
||||
? PeerFloodType::InviteGroup
|
||||
: PeerFloodType::InviteChannel;
|
||||
const auto text = PeerFloodErrorText(&chat->session(), type);
|
||||
Ui::show(Box<Ui::InformBox>(text), Ui::LayerOption::KeepOther);
|
||||
return;
|
||||
}
|
||||
const auto text = [&] {
|
||||
if (error == u"USER_BOT"_q) {
|
||||
return tr::lng_cant_invite_bot_to_channel(tr::now);
|
||||
@@ -184,11 +196,6 @@ void ShowAddParticipantsError(
|
||||
return tr::lng_bot_already_in_group(tr::now);
|
||||
} else if (error == u"BOT_GROUPS_BLOCKED"_q) {
|
||||
return tr::lng_error_cant_add_bot(tr::now);
|
||||
} else if (error == u"PEER_FLOOD"_q) {
|
||||
const auto type = (chat->isChat() || chat->isMegagroup())
|
||||
? PeerFloodType::InviteGroup
|
||||
: PeerFloodType::InviteChannel;
|
||||
return PeerFloodErrorText(&chat->session(), type);
|
||||
} else if (error == u"ADMINS_TOO_MUCH"_q) {
|
||||
return ((chat->isChat() || chat->isMegagroup())
|
||||
? tr::lng_error_admin_limit
|
||||
@@ -1521,11 +1528,10 @@ RevokePublicLinkBox::Inner::Inner(
|
||||
st::contactsNameStyle,
|
||||
peer->name,
|
||||
Ui::NameTextOptions());
|
||||
row.status.setText(
|
||||
row.status.setMarkedText(
|
||||
st::defaultTextStyle,
|
||||
_session->createInternalLink(
|
||||
textcmdLink(1, peer->userName())),
|
||||
Ui::DialogTextOptions());
|
||||
Ui::Text::Link(peer->userName())));
|
||||
_rows.push_back(std::move(row));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ enum class PeerFloodType {
|
||||
|
||||
[[nodiscard]] style::InputField CreateBioFieldStyle();
|
||||
|
||||
[[nodiscard]] QString PeerFloodErrorText(
|
||||
[[nodiscard]] TextWithEntities PeerFloodErrorText(
|
||||
not_null<Main::Session*> session,
|
||||
PeerFloodType type);
|
||||
void ShowAddParticipantsError(
|
||||
|
||||
@@ -302,7 +302,7 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
|
||||
base::unixtime::now(),
|
||||
out ? history->session().userId() : peerToUser(history->peer->id),
|
||||
QString(),
|
||||
TextWithEntities{ TextUtilities::Clean(text) },
|
||||
TextWithEntities{ text },
|
||||
MTP_messageMediaEmpty(),
|
||||
HistoryMessageMarkupData(),
|
||||
groupedId);
|
||||
@@ -321,15 +321,11 @@ bool ServiceCheck::checkRippleStartPosition(QPoint position) const {
|
||||
const auto takeHeight = (width > height)
|
||||
? size
|
||||
: (height * size / width);
|
||||
return Images::prepare(
|
||||
image,
|
||||
takeWidth * cIntRetinaFactor(),
|
||||
takeHeight * cIntRetinaFactor(),
|
||||
Images::Option::Smooth
|
||||
| Images::Option::TransparentBackground
|
||||
| blur,
|
||||
size,
|
||||
size);
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
return Images::Prepare(image, QSize(takeWidth, takeHeight) * ratio, {
|
||||
.options = Images::Option::TransparentBackground | blur,
|
||||
.outer = { size, size },
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage PrepareScaledFromFull(
|
||||
@@ -667,7 +663,7 @@ void BackgroundPreviewBox::setScaledFromThumb() {
|
||||
_paper.backgroundColors(),
|
||||
_paper.gradientRotation(),
|
||||
_paper.patternOpacity(),
|
||||
_paper.document() ? Images::Option::Blurred : Images::Option(0));
|
||||
_paper.document() ? Images::Option::Blur : Images::Option());
|
||||
auto blurred = (_paper.document() || _paper.isPattern())
|
||||
? QImage()
|
||||
: PrepareScaledNonPattern(
|
||||
|
||||
@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/basic_click_handlers.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -312,9 +313,12 @@ void ProxyRow::updateFields(View &&view) {
|
||||
}
|
||||
_view = std::move(view);
|
||||
const auto endpoint = _view.host + ':' + QString::number(_view.port);
|
||||
_title.setText(
|
||||
_title.setMarkedText(
|
||||
st::proxyRowTitleStyle,
|
||||
_view.type + ' ' + textcmdLink(1, endpoint),
|
||||
TextWithEntities()
|
||||
.append(_view.type)
|
||||
.append(' ')
|
||||
.append(Ui::Text::Link(endpoint, QString())),
|
||||
Ui::ItemTextDefaultOptions());
|
||||
|
||||
const auto state = _view.state;
|
||||
@@ -522,7 +526,7 @@ void ProxyRow::showMenu() {
|
||||
const QString &text,
|
||||
Fn<void()> callback,
|
||||
const style::icon *icon) {
|
||||
return _menu->addAction(text, std::move(callback));
|
||||
return _menu->addAction(text, std::move(callback), icon);
|
||||
};
|
||||
addAction(tr::lng_proxy_menu_edit(tr::now), [=] {
|
||||
_editClicks.fire({});
|
||||
|
||||
@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "main/main_session.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
@@ -190,9 +191,12 @@ not_null<Ui::FlatLabel*> CreateWarningLabel(
|
||||
const auto value = valueLimit - length;
|
||||
const auto shown = (value < warnLimit)
|
||||
&& (field->height() > st::createPollOptionField.heightMin);
|
||||
result->setRichText((value >= 0)
|
||||
? QString::number(value)
|
||||
: textcmdLink(1, QString::number(value)));
|
||||
if (value >= 0) {
|
||||
result->setText(QString::number(value));
|
||||
} else {
|
||||
result->setMarkedText(Ui::Text::PlainLink(
|
||||
QString::number(value)));
|
||||
}
|
||||
result->setVisible(shown);
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -156,6 +156,12 @@ void DeleteMessagesBox::prepare() {
|
||||
: tr::lng_box_leave();
|
||||
}, _revoke->lifetime());
|
||||
}
|
||||
} else if (canDelete
|
||||
&& _wipeHistoryJustClear
|
||||
&& (peer->isMegagroup() || peer->isChat())) {
|
||||
appendDetails({
|
||||
tr::lng_delete_clear_for_me(tr::now)
|
||||
});
|
||||
}
|
||||
} else if (_moderateFrom) {
|
||||
Assert(_moderateInChannel != nullptr);
|
||||
|
||||
@@ -28,8 +28,7 @@ namespace {
|
||||
TextParseOptions kInformBoxTextOptions = {
|
||||
(TextParseLinks
|
||||
| TextParseMultiline
|
||||
| TextParseMarkdown
|
||||
| TextParseRichText), // flags
|
||||
| TextParseMarkdown), // flags
|
||||
0, // maxw
|
||||
0, // maxh
|
||||
Qt::LayoutDirectionAuto, // dir
|
||||
|
||||
@@ -32,7 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_passport.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "base/qt_adapters.h"
|
||||
#include "base/qt/qt_common_adapters.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_message_reactions.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "history/admin_log/history_admin_log_section.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "lang/lang_keys.h"
|
||||
@@ -1060,7 +1061,7 @@ void Controller::fillManageSection() {
|
||||
!_peer->isBroadcast(),
|
||||
session->data().reactions().list(
|
||||
Data::Reactions::Type::Active),
|
||||
session->data().reactions().list(_peer),
|
||||
*Data::PeerAllowedReactions(_peer),
|
||||
done));
|
||||
},
|
||||
st::infoIconReactions);
|
||||
|
||||
@@ -1116,7 +1116,8 @@ void ShareInviteLinkBox(not_null<PeerData*> peer, const QString &link) {
|
||||
auto submitCallback = [=](
|
||||
std::vector<not_null<PeerData*>> &&result,
|
||||
TextWithTags &&comment,
|
||||
Api::SendOptions options) {
|
||||
Api::SendOptions options,
|
||||
Data::ForwardOptions) {
|
||||
if (*sending || result.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "lottie/lottie_icon.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
@@ -32,6 +33,7 @@ void AddReactionIcon(
|
||||
not_null<DocumentData*> document) {
|
||||
struct State {
|
||||
std::shared_ptr<Data::DocumentMedia> media;
|
||||
std::unique_ptr<Lottie::Icon> icon;
|
||||
QImage image;
|
||||
};
|
||||
|
||||
@@ -45,39 +47,42 @@ void AddReactionIcon(
|
||||
button->sizeValue(
|
||||
) | rpl::start_with_next([=](QSize size) {
|
||||
icon->moveToLeft(
|
||||
st::settingsSectionIconLeft,
|
||||
st::editPeerReactionsIconLeft,
|
||||
(size.height() - icon->height()) / 2,
|
||||
size.width());
|
||||
}, icon->lifetime());
|
||||
|
||||
const auto setImage = [=](not_null<Image*> image) {
|
||||
state->image = Images::prepare(
|
||||
image->original(),
|
||||
size * style::DevicePixelRatio(),
|
||||
size * style::DevicePixelRatio(),
|
||||
Images::Option::Smooth | Images::Option::TransparentBackground,
|
||||
size,
|
||||
size);
|
||||
icon->update();
|
||||
const auto initLottie = [=] {
|
||||
state->icon = std::make_unique<Lottie::Icon>(Lottie::IconDescriptor{
|
||||
.path = state->media->owner()->filepath(true),
|
||||
.json = state->media->bytes(),
|
||||
.sizeOverride = QSize(size, size),
|
||||
.frame = -1,
|
||||
});
|
||||
state->media = nullptr;
|
||||
};
|
||||
if (const auto image = state->media->getStickerLarge()) {
|
||||
setImage(image);
|
||||
state->media->checkStickerLarge();
|
||||
if (state->media->loaded()) {
|
||||
initLottie();
|
||||
} else {
|
||||
document->session().downloaderTaskFinished(
|
||||
) | rpl::map([=] {
|
||||
return state->media->getStickerLarge();
|
||||
}) | rpl::filter_nullptr() | rpl::take(
|
||||
1
|
||||
) | rpl::start_with_next([=](not_null<Image*> image) {
|
||||
setImage(image);
|
||||
}, button->lifetime());
|
||||
) | rpl::filter([=] {
|
||||
return state->media->loaded();
|
||||
}) | rpl::take(1) | rpl::start_with_next([=] {
|
||||
initLottie();
|
||||
icon->update();
|
||||
}, icon->lifetime());
|
||||
}
|
||||
|
||||
icon->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
Painter p(icon);
|
||||
QPainter p(icon);
|
||||
if (state->image.isNull() && state->icon) {
|
||||
state->image = state->icon->frame();
|
||||
crl::async([icon = std::move(state->icon)]{});
|
||||
}
|
||||
if (!state->image.isNull()) {
|
||||
p.drawImage(0, 0, state->image);
|
||||
p.drawImage(QRect(0, 0, size, size), state->image);
|
||||
}
|
||||
}, icon->lifetime());
|
||||
}
|
||||
@@ -88,7 +93,7 @@ void EditAllowedReactionsBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
bool isGroup,
|
||||
const std::vector<Reaction> &list,
|
||||
const std::vector<Reaction> &selected,
|
||||
const base::flat_set<QString> &selected,
|
||||
Fn<void(const std::vector<QString> &)> callback) {
|
||||
box->setTitle(tr::lng_manage_peer_reactions());
|
||||
|
||||
@@ -141,14 +146,16 @@ void EditAllowedReactionsBox(
|
||||
tr::lng_manage_peer_reactions_available());
|
||||
|
||||
const auto active = [&](const Data::Reaction &entry) {
|
||||
return ranges::contains(selected, entry.emoji, &Reaction::emoji);
|
||||
return selected.contains(entry.emoji);
|
||||
};
|
||||
const auto add = [&](const Data::Reaction &entry) {
|
||||
const auto button = Settings::AddButton(
|
||||
container,
|
||||
rpl::single(entry.title),
|
||||
st::manageGroupButton.button);
|
||||
AddReactionIcon(button, entry.staticIcon);
|
||||
AddReactionIcon(button, entry.centerIcon
|
||||
? entry.centerIcon
|
||||
: entry.appearAnimation.get());
|
||||
state->toggles.emplace(entry.emoji, button);
|
||||
button->toggleOn(rpl::single(
|
||||
active(entry)
|
||||
@@ -191,9 +198,9 @@ void SaveAllowedReactions(
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
peer->session().api().applyUpdates(result);
|
||||
if (const auto chat = peer->asChat()) {
|
||||
chat->setAllowedReactions(allowed);
|
||||
chat->setAllowedReactions({ begin(allowed), end(allowed) });
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
channel->setAllowedReactions(allowed);
|
||||
channel->setAllowedReactions({ begin(allowed), end(allowed) });
|
||||
} else {
|
||||
Unexpected("Invalid peer type in SaveAllowedReactions.");
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ void EditAllowedReactionsBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
bool isGroup,
|
||||
const std::vector<Data::Reaction> &list,
|
||||
const std::vector<Data::Reaction> &selected,
|
||||
const base::flat_set<QString> &selected,
|
||||
Fn<void(const std::vector<QString> &)> callback);
|
||||
|
||||
void SaveAllowedReactions(
|
||||
|
||||
@@ -183,11 +183,10 @@ void PeerShortInfoCover::paint(QPainter &p) {
|
||||
_widget->size() * style::DevicePixelRatio(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
image.fill(Qt::black);
|
||||
Images::prepareRound(
|
||||
image,
|
||||
_userpicImage = Images::Round(
|
||||
std::move(image),
|
||||
ImageRoundRadius::Small,
|
||||
RectPart::TopLeft | RectPart::TopRight);
|
||||
_userpicImage = std::move(image);
|
||||
}
|
||||
|
||||
paintCoverImage(p, frame.isNull() ? _userpicImage : frame);
|
||||
@@ -229,8 +228,8 @@ void PeerShortInfoCover::paintCoverImage(QPainter &p, const QImage &image) {
|
||||
image,
|
||||
QRect(0, from * factor, roundedWidth * factor, rounded * factor));
|
||||
q.end();
|
||||
Images::prepareRound(
|
||||
_roundedTopImage,
|
||||
_roundedTopImage = Images::Round(
|
||||
std::move(_roundedTopImage),
|
||||
ImageRoundRadius::Small,
|
||||
RectPart::TopLeft | RectPart::TopRight);
|
||||
p.drawImage(
|
||||
@@ -244,9 +243,8 @@ void PeerShortInfoCover::paintBars(QPainter &p) {
|
||||
const auto factor = style::DevicePixelRatio();
|
||||
if (_shadowTop.isNull()) {
|
||||
_shadowTop = Images::GenerateShadow(height, kShadowMaxAlpha, 0);
|
||||
_shadowTop = _shadowTop.scaled(QSize(_st.size, height) * factor);
|
||||
Images::prepareRound(
|
||||
_shadowTop,
|
||||
_shadowTop = Images::Round(
|
||||
_shadowTop.scaled(QSize(_st.size, height) * factor),
|
||||
ImageRoundRadius::Small,
|
||||
RectPart::TopLeft | RectPart::TopRight);
|
||||
}
|
||||
@@ -771,8 +769,8 @@ int PeerShortInfoBox::fillRoundedTopHeight() {
|
||||
void PeerShortInfoBox::refreshRoundedTopImage(const QColor &color) {
|
||||
_roundedTopColor = color;
|
||||
_roundedTop.fill(color);
|
||||
Images::prepareRound(
|
||||
_roundedTop,
|
||||
_roundedTop = Images::Round(
|
||||
std::move(_roundedTop),
|
||||
ImageRoundRadius::Small,
|
||||
RectPart::TopLeft | RectPart::TopRight);
|
||||
}
|
||||
|
||||
@@ -51,19 +51,15 @@ void GenerateImage(
|
||||
bool blurred = false) {
|
||||
using namespace Images;
|
||||
const auto size = st::shortInfoWidth;
|
||||
const auto factor = style::DevicePixelRatio();
|
||||
const auto options = Option::Smooth
|
||||
| Option::RoundedSmall
|
||||
| Option::RoundedTopLeft
|
||||
| Option::RoundedTopRight
|
||||
| (blurred ? Option::Blurred : Option());
|
||||
state->current.photo = Images::prepare(
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto options = Option::RoundSmall
|
||||
| Option::RoundSkipBottomLeft
|
||||
| Option::RoundSkipBottomRight
|
||||
| (blurred ? Option::Blur : Option());
|
||||
state->current.photo = Images::Prepare(
|
||||
std::move(image),
|
||||
size * factor,
|
||||
size * factor,
|
||||
options,
|
||||
size,
|
||||
size);
|
||||
QSize(size, size) * ratio,
|
||||
{ .options = options, .outer = { size, size } });
|
||||
}
|
||||
|
||||
void GenerateImage(
|
||||
|
||||
@@ -289,12 +289,9 @@ void RenameBox(not_null<Ui::GenericBox*> box) {
|
||||
Unexpected("Type in LottieForType.");
|
||||
}();
|
||||
const auto size = st::sessionBigLottieSize;
|
||||
static const auto kWhite = style::owned_color(Qt::white);
|
||||
return std::make_unique<Lottie::Icon>(Lottie::IconDescriptor{
|
||||
.path = u":/icons/settings/devices/"_q + path + u".lottie"_q,
|
||||
.color = kWhite.color(),
|
||||
.sizeOverride = QSize(size, size),
|
||||
.frame = 1,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -360,7 +357,7 @@ void RenameBox(not_null<Ui::GenericBox*> box) {
|
||||
state->lottie->animate(
|
||||
[=] { result->update(); },
|
||||
0,
|
||||
state->lottie->framesCount());
|
||||
state->lottie->framesCount() - 1);
|
||||
}, result->lifetime());
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/storage_account.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "apiwrap.h"
|
||||
#include "ui/chat/forward_options_box.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/multi_select.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/menu/menu_action.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
@@ -40,6 +44,44 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_media_player.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class ForwardOptionItem final : public Ui::Menu::Action {
|
||||
public:
|
||||
using Ui::Menu::Action::Action;
|
||||
|
||||
void init(bool checked) {
|
||||
enableMouseSelecting();
|
||||
|
||||
AbstractButton::setDisabled(true);
|
||||
|
||||
_checkView = std::make_unique<Ui::CheckView>(st::defaultCheck, false);
|
||||
_checkView->checkedChanges(
|
||||
) | rpl::start_with_next([=](bool checked) {
|
||||
setIcon(checked ? &st::mediaPlayerMenuCheck : nullptr);
|
||||
}, lifetime());
|
||||
|
||||
_checkView->setChecked(checked, anim::type::normal);
|
||||
AbstractButton::clicks(
|
||||
) | rpl::start_with_next([=] {
|
||||
_checkView->setChecked(
|
||||
!_checkView->checked(),
|
||||
anim::type::normal);
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
not_null<Ui::CheckView*> checkView() const {
|
||||
return _checkView.get();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::CheckView> _checkView;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class ShareBox::Inner final : public Ui::RpWidget {
|
||||
public:
|
||||
@@ -442,17 +484,68 @@ SendMenu::Type ShareBox::sendMenuType() const {
|
||||
: SendMenu::Type::Scheduled;
|
||||
}
|
||||
|
||||
void ShareBox::showMenu(not_null<Ui::RpWidget*> parent) {
|
||||
if (_menu) {
|
||||
_menu = nullptr;
|
||||
return;
|
||||
}
|
||||
_menu.emplace(parent, st::popupMenuWithIcons);
|
||||
|
||||
if (_descriptor.forwardOptions.show) {
|
||||
auto createView = [&](rpl::producer<QString> &&text, bool checked) {
|
||||
auto item = base::make_unique_q<ForwardOptionItem>(
|
||||
_menu->menu(),
|
||||
st::popupMenuWithIcons.menu,
|
||||
new QAction(QString(), _menu->menu()),
|
||||
nullptr,
|
||||
nullptr);
|
||||
std::move(
|
||||
text
|
||||
) | rpl::start_with_next([action = item->action()](QString text) {
|
||||
action->setText(text);
|
||||
}, item->lifetime());
|
||||
item->init(checked);
|
||||
const auto view = item->checkView();
|
||||
_menu->addAction(std::move(item));
|
||||
return view;
|
||||
};
|
||||
Ui::FillForwardOptions(
|
||||
std::move(createView),
|
||||
_descriptor.forwardOptions.messagesCount,
|
||||
_forwardOptions,
|
||||
[=](Ui::ForwardOptions value) { _forwardOptions = value; },
|
||||
_menu->lifetime());
|
||||
|
||||
_menu->addSeparator();
|
||||
}
|
||||
|
||||
const auto result = SendMenu::FillSendMenu(
|
||||
_menu.get(),
|
||||
sendMenuType(),
|
||||
[=] { submitSilent(); },
|
||||
[=] { submitScheduled(); });
|
||||
const auto success = (result == SendMenu::FillMenuResult::Success);
|
||||
if (_descriptor.forwardOptions.show || success) {
|
||||
_menu->setForcedOrigin(Ui::PanelAnimation::Origin::BottomRight);
|
||||
_menu->popup(QCursor::pos());
|
||||
}
|
||||
}
|
||||
|
||||
void ShareBox::createButtons() {
|
||||
clearButtons();
|
||||
if (_hasSelected) {
|
||||
const auto send = addButton(tr::lng_share_confirm(), [=] {
|
||||
submit({});
|
||||
});
|
||||
SendMenu::SetupMenuAndShortcuts(
|
||||
send,
|
||||
[=] { return sendMenuType(); },
|
||||
[=] { submitSilent(); },
|
||||
[=] { submitScheduled(); });
|
||||
_forwardOptions.hasCaptions = _descriptor.forwardOptions.hasCaptions;
|
||||
|
||||
send->setAcceptBoth();
|
||||
send->clicks(
|
||||
) | rpl::start_with_next([=](Qt::MouseButton button) {
|
||||
if (button == Qt::RightButton) {
|
||||
showMenu(send);
|
||||
}
|
||||
}, send->lifetime());
|
||||
} else if (_descriptor.copyCallback) {
|
||||
addButton(_copyLinkText.value(), [=] { copyLink(); });
|
||||
}
|
||||
@@ -488,10 +581,17 @@ void ShareBox::innerSelectedChanged(PeerData *peer, bool checked) {
|
||||
|
||||
void ShareBox::submit(Api::SendOptions options) {
|
||||
if (const auto onstack = _descriptor.submitCallback) {
|
||||
const auto forwardOptions = (_forwardOptions.hasCaptions
|
||||
&& _forwardOptions.dropCaptions)
|
||||
? Data::ForwardOptions::NoNamesAndCaptions
|
||||
: _forwardOptions.dropNames
|
||||
? Data::ForwardOptions::NoSenderNames
|
||||
: Data::ForwardOptions::PreserveInfo;
|
||||
onstack(
|
||||
_inner->selected(),
|
||||
_comment->entity()->getTextWithAppliedMarkdown(),
|
||||
options);
|
||||
options,
|
||||
forwardOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "base/observer.h"
|
||||
#include "base/timer.h"
|
||||
#include "ui/chat/forward_options_box.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/effects/round_checkbox.h"
|
||||
#include "mtproto/sender.h"
|
||||
@@ -41,12 +42,17 @@ class Row;
|
||||
class IndexedList;
|
||||
} // namespace Dialogs
|
||||
|
||||
namespace Data {
|
||||
enum class ForwardOptions;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
class MultiSelect;
|
||||
class InputField;
|
||||
struct ScrollToRequest;
|
||||
template <typename Widget>
|
||||
class SlideWrap;
|
||||
class PopupMenu;
|
||||
} // namespace Ui
|
||||
|
||||
QString AppendShareGameScoreUrl(
|
||||
@@ -63,7 +69,8 @@ public:
|
||||
using SubmitCallback = Fn<void(
|
||||
std::vector<not_null<PeerData*>>&&,
|
||||
TextWithTags&&,
|
||||
Api::SendOptions)>;
|
||||
Api::SendOptions,
|
||||
Data::ForwardOptions option)>;
|
||||
using FilterCallback = Fn<bool(PeerData*)>;
|
||||
|
||||
struct Descriptor {
|
||||
@@ -79,6 +86,11 @@ public:
|
||||
const style::MultiSelect *stMultiSelect = nullptr;
|
||||
const style::InputField *stComment = nullptr;
|
||||
const style::PeerList *st = nullptr;
|
||||
struct {
|
||||
int messagesCount = 0;
|
||||
bool show = false;
|
||||
bool hasCaptions = false;
|
||||
} forwardOptions;
|
||||
};
|
||||
ShareBox(QWidget*, Descriptor &&descriptor);
|
||||
|
||||
@@ -119,6 +131,8 @@ private:
|
||||
mtpRequestId requestId);
|
||||
void peopleFail(const MTP::Error &error, mtpRequestId requestId);
|
||||
|
||||
void showMenu(not_null<Ui::RpWidget*> parent);
|
||||
|
||||
Descriptor _descriptor;
|
||||
MTP::Sender _api;
|
||||
|
||||
@@ -126,6 +140,9 @@ private:
|
||||
object_ptr<Ui::SlideWrap<Ui::InputField>> _comment;
|
||||
object_ptr<Ui::RpWidget> _bottomWidget;
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
Ui::ForwardOptions _forwardOptions;
|
||||
|
||||
class Inner;
|
||||
QPointer<Inner> _inner;
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lottie/lottie_multi_player.h"
|
||||
#include "lottie/lottie_animation.h"
|
||||
#include "chat_helpers/stickers_lottie.h"
|
||||
#include "media/clip/media_clip_reader.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "main/main_session.h"
|
||||
@@ -51,6 +52,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace {
|
||||
|
||||
constexpr auto kStickersPanelPerRow = 5;
|
||||
constexpr auto kMinRepaintDelay = crl::time(33);
|
||||
constexpr auto kMinAfterScrollDelay = crl::time(33);
|
||||
|
||||
using Data::StickersSet;
|
||||
using Data::StickersPack;
|
||||
@@ -99,7 +102,8 @@ private:
|
||||
struct Element {
|
||||
not_null<DocumentData*> document;
|
||||
std::shared_ptr<Data::DocumentMedia> documentMedia;
|
||||
Lottie::Animation *animated = nullptr;
|
||||
Lottie::Animation *lottie = nullptr;
|
||||
Media::Clip::ReaderPointer webm;
|
||||
Ui::Animations::Simple overAnimation;
|
||||
};
|
||||
|
||||
@@ -107,8 +111,18 @@ private:
|
||||
|
||||
QSize boundingBoxSize() const;
|
||||
|
||||
void paintSticker(Painter &p, int index, QPoint position) const;
|
||||
void paintSticker(
|
||||
Painter &p,
|
||||
int index,
|
||||
QPoint position,
|
||||
bool paused,
|
||||
crl::time now) const;
|
||||
void setupLottie(int index);
|
||||
void setupWebm(int index);
|
||||
void clipCallback(
|
||||
Media::Clip::Notification notification,
|
||||
not_null<DocumentData*> document,
|
||||
int index);
|
||||
|
||||
void updateSelected();
|
||||
void setSelected(int selected);
|
||||
@@ -123,6 +137,8 @@ private:
|
||||
not_null<Lottie::MultiPlayer*> getLottiePlayer();
|
||||
|
||||
void showPreview();
|
||||
void updateItems();
|
||||
void repaintItems(crl::time now = 0);
|
||||
|
||||
not_null<Window::SessionController*> _controller;
|
||||
MTP::Sender _api;
|
||||
@@ -142,6 +158,12 @@ private:
|
||||
|
||||
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
||||
|
||||
int _visibleTop = 0;
|
||||
int _visibleBottom = 0;
|
||||
crl::time _lastScrolledAt = 0;
|
||||
crl::time _lastUpdatedAt = 0;
|
||||
base::Timer _updateItemsTimer;
|
||||
|
||||
StickerSetIdentifier _input;
|
||||
|
||||
mtpRequestId _installRequest = 0;
|
||||
@@ -370,9 +392,12 @@ StickerSetBox::Inner::Inner(
|
||||
, _pathGradient(std::make_unique<Ui::PathShiftGradient>(
|
||||
st::windowBgRipple,
|
||||
st::windowBgOver,
|
||||
[=] { update(); }))
|
||||
[=] { repaintItems(); }))
|
||||
, _updateItemsTimer([=] { updateItems(); })
|
||||
, _input(set)
|
||||
, _previewTimer([=] { showPreview(); }) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
|
||||
_api.request(MTPmessages_GetStickerSet(
|
||||
Data::InputStickerSet(_input),
|
||||
MTP_int(0) // hash
|
||||
@@ -387,7 +412,7 @@ StickerSetBox::Inner::Inner(
|
||||
|
||||
_controller->session().downloaderTaskFinished(
|
||||
) | rpl::start_with_next([=] {
|
||||
update();
|
||||
updateItems();
|
||||
}, lifetime());
|
||||
|
||||
setMouseTracking(true);
|
||||
@@ -735,7 +760,7 @@ not_null<Lottie::MultiPlayer*> StickerSetBox::Inner::getLottiePlayer() {
|
||||
Lottie::MakeFrameRenderer());
|
||||
_lottiePlayer->updates(
|
||||
) | rpl::start_with_next([=] {
|
||||
update();
|
||||
updateItems();
|
||||
}, lifetime());
|
||||
}
|
||||
return _lottiePlayer.get();
|
||||
@@ -756,6 +781,7 @@ int32 StickerSetBox::Inner::stickerFromGlobalPos(const QPoint &p) const {
|
||||
void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
p.fillRect(e->rect(), st::boxBg);
|
||||
if (_elements.empty()) {
|
||||
return;
|
||||
}
|
||||
@@ -764,6 +790,9 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
|
||||
_pathGradient->startFrame(0, width(), width() / 2);
|
||||
|
||||
const auto now = crl::now();
|
||||
const auto paused = _controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Layer);
|
||||
for (int32 i = from; i < to; ++i) {
|
||||
for (int32 j = 0; j < kStickersPanelPerRow; ++j) {
|
||||
int32 index = i * kStickersPanelPerRow + j;
|
||||
@@ -771,16 +800,12 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
break;
|
||||
}
|
||||
const auto pos = QPoint(st::stickersPadding.left() + j * st::stickersSize.width(), st::stickersPadding.top() + i * st::stickersSize.height());
|
||||
paintSticker(p, index, pos);
|
||||
paintSticker(p, index, pos, paused, now);
|
||||
}
|
||||
}
|
||||
|
||||
if (_lottiePlayer) {
|
||||
const auto paused = _controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Layer);
|
||||
if (!paused) {
|
||||
_lottiePlayer->markFrameShown();
|
||||
}
|
||||
if (_lottiePlayer && !paused) {
|
||||
_lottiePlayer->markFrameShown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -793,6 +818,12 @@ QSize StickerSetBox::Inner::boundingBoxSize() const {
|
||||
void StickerSetBox::Inner::visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) {
|
||||
if (_visibleTop != visibleTop || _visibleBottom != visibleBottom) {
|
||||
_visibleTop = visibleTop;
|
||||
_visibleBottom = visibleBottom;
|
||||
_lastScrolledAt = crl::now();
|
||||
update();
|
||||
}
|
||||
const auto pauseInRows = [&](int fromRow, int tillRow) {
|
||||
Expects(fromRow <= tillRow);
|
||||
|
||||
@@ -802,8 +833,10 @@ void StickerSetBox::Inner::visibleTopBottomUpdated(
|
||||
if (index >= _elements.size()) {
|
||||
break;
|
||||
}
|
||||
if (const auto animated = _elements[index].animated) {
|
||||
_lottiePlayer->pause(animated);
|
||||
if (const auto lottie = _elements[index].lottie) {
|
||||
_lottiePlayer->pause(lottie);
|
||||
} else if (auto &webm = _elements[index].webm) {
|
||||
webm = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -834,17 +867,63 @@ void StickerSetBox::Inner::visibleTopBottomUpdated(
|
||||
void StickerSetBox::Inner::setupLottie(int index) {
|
||||
auto &element = _elements[index];
|
||||
|
||||
element.animated = ChatHelpers::LottieAnimationFromDocument(
|
||||
element.lottie = ChatHelpers::LottieAnimationFromDocument(
|
||||
getLottiePlayer(),
|
||||
element.documentMedia.get(),
|
||||
ChatHelpers::StickerLottieSize::StickerSet,
|
||||
boundingBoxSize() * cIntRetinaFactor());
|
||||
}
|
||||
|
||||
void StickerSetBox::Inner::setupWebm(int index) {
|
||||
auto &element = _elements[index];
|
||||
|
||||
const auto document = element.document;
|
||||
auto callback = [=](Media::Clip::Notification notification) {
|
||||
clipCallback(notification, document, index);
|
||||
};
|
||||
element.webm = Media::Clip::MakeReader(
|
||||
element.documentMedia->owner()->location(),
|
||||
element.documentMedia->bytes(),
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
void StickerSetBox::Inner::clipCallback(
|
||||
Media::Clip::Notification notification,
|
||||
not_null<DocumentData*> document,
|
||||
int index) {
|
||||
const auto i = (index < _elements.size()
|
||||
&& _elements[index].document == document)
|
||||
? (_elements.begin() + index)
|
||||
: ranges::find(_elements, document, &Element::document);
|
||||
if (i == end(_elements)) {
|
||||
return;
|
||||
}
|
||||
using namespace Media::Clip;
|
||||
switch (notification) {
|
||||
case Notification::Reinit: {
|
||||
auto &webm = i->webm;
|
||||
if (webm->state() == State::Error) {
|
||||
webm.setBad();
|
||||
} else if (webm->ready() && !webm->started()) {
|
||||
const auto size = ChatHelpers::ComputeStickerSize(
|
||||
i->document,
|
||||
boundingBoxSize());
|
||||
webm->start({ .frame = size, .keepAlpha = true });
|
||||
}
|
||||
} break;
|
||||
|
||||
case Notification::Repaint: break;
|
||||
}
|
||||
|
||||
updateItems();
|
||||
}
|
||||
|
||||
void StickerSetBox::Inner::paintSticker(
|
||||
Painter &p,
|
||||
int index,
|
||||
QPoint position) const {
|
||||
QPoint position,
|
||||
bool paused,
|
||||
crl::time now) const {
|
||||
if (const auto over = _elements[index].overAnimation.value((index == _selected) ? 1. : 0.)) {
|
||||
p.setOpacity(over);
|
||||
auto tl = position;
|
||||
@@ -856,47 +935,46 @@ void StickerSetBox::Inner::paintSticker(
|
||||
const auto &element = _elements[index];
|
||||
const auto document = element.document;
|
||||
const auto &media = element.documentMedia;
|
||||
const auto sticker = document->sticker();
|
||||
media->checkStickerSmall();
|
||||
|
||||
const auto isAnimated = document->sticker()->animated;
|
||||
if (isAnimated
|
||||
&& !element.animated
|
||||
&& media->loaded()) {
|
||||
const_cast<Inner*>(this)->setupLottie(index);
|
||||
if (media->loaded()) {
|
||||
if (sticker->isLottie() && !element.lottie) {
|
||||
const_cast<Inner*>(this)->setupLottie(index);
|
||||
} else if (sticker->isWebm() && !element.webm) {
|
||||
const_cast<Inner*>(this)->setupWebm(index);
|
||||
}
|
||||
}
|
||||
|
||||
auto w = 1;
|
||||
auto h = 1;
|
||||
if (isAnimated && !document->dimensions.isEmpty()) {
|
||||
const auto request = Lottie::FrameRequest{ boundingBoxSize() * cIntRetinaFactor() };
|
||||
const auto size = request.size(document->dimensions, true) / cIntRetinaFactor();
|
||||
w = std::max(size.width(), 1);
|
||||
h = std::max(size.height(), 1);
|
||||
} else {
|
||||
auto coef = qMin((st::stickersSize.width() - st::roundRadiusSmall * 2) / float64(document->dimensions.width()), (st::stickersSize.height() - st::roundRadiusSmall * 2) / float64(document->dimensions.height()));
|
||||
if (coef > 1) coef = 1;
|
||||
w = std::max(qRound(coef * document->dimensions.width()), 1);
|
||||
h = std::max(qRound(coef * document->dimensions.height()), 1);
|
||||
}
|
||||
QPoint ppos = position + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2);
|
||||
const auto size = ChatHelpers::ComputeStickerSize(
|
||||
document,
|
||||
boundingBoxSize());
|
||||
const auto ppos = position + QPoint(
|
||||
(st::stickersSize.width() - size.width()) / 2,
|
||||
(st::stickersSize.height() - size.height()) / 2);
|
||||
|
||||
if (element.animated && element.animated->ready()) {
|
||||
const auto frame = element.animated->frame();
|
||||
if (element.lottie && element.lottie->ready()) {
|
||||
const auto frame = element.lottie->frame();
|
||||
p.drawImage(
|
||||
QRect(ppos, frame.size() / cIntRetinaFactor()),
|
||||
frame);
|
||||
|
||||
_lottiePlayer->unpause(element.animated);
|
||||
_lottiePlayer->unpause(element.lottie);
|
||||
} else if (element.webm && element.webm->started()) {
|
||||
p.drawPixmap(ppos, element.webm->current({
|
||||
.frame = size,
|
||||
.keepAlpha = true,
|
||||
}, paused ? 0 : now));
|
||||
} else if (const auto image = media->getStickerSmall()) {
|
||||
p.drawPixmapLeft(
|
||||
ppos,
|
||||
width(),
|
||||
image->pix(w, h));
|
||||
image->pix(size));
|
||||
} else {
|
||||
ChatHelpers::PaintStickerThumbnailPath(
|
||||
p,
|
||||
media.get(),
|
||||
QRect(ppos, QSize(w, h)),
|
||||
QRect(ppos, size),
|
||||
_pathGradient.get());
|
||||
}
|
||||
}
|
||||
@@ -965,4 +1043,23 @@ void StickerSetBox::Inner::archiveStickers() {
|
||||
}).send();
|
||||
}
|
||||
|
||||
void StickerSetBox::Inner::updateItems() {
|
||||
const auto now = crl::now();
|
||||
|
||||
const auto delay = std::max(
|
||||
_lastScrolledAt + kMinAfterScrollDelay - now,
|
||||
_lastUpdatedAt + kMinRepaintDelay - now);
|
||||
if (delay <= 0) {
|
||||
repaintItems(now);
|
||||
} else if (!_updateItemsTimer.isActive()
|
||||
|| _updateItemsTimer.remainingTime() > kMinRepaintDelay) {
|
||||
_updateItemsTimer.callOnce(std::max(delay, kMinRepaintDelay));
|
||||
}
|
||||
}
|
||||
|
||||
void StickerSetBox::Inner::repaintItems(crl::time now) {
|
||||
_lastUpdatedAt = now ? now : crl::now();
|
||||
update();
|
||||
}
|
||||
|
||||
StickerSetBox::Inner::~Inner() = default;
|
||||
|
||||
@@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "media/clip/media_clip_reader.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -143,10 +144,7 @@ private:
|
||||
int32 count,
|
||||
const QString &title,
|
||||
int titleWidth,
|
||||
bool installed,
|
||||
bool official,
|
||||
bool unread,
|
||||
bool archived,
|
||||
Data::StickersSetFlags flagsOverride,
|
||||
bool removed,
|
||||
int32 pixw,
|
||||
int32 pixh);
|
||||
@@ -154,6 +152,10 @@ private:
|
||||
|
||||
bool isRecentSet() const;
|
||||
bool isMasksSet() const;
|
||||
bool isWebm() const;
|
||||
bool isInstalled() const;
|
||||
bool isUnread() const;
|
||||
bool isArchived() const;
|
||||
|
||||
const not_null<StickersSet*> set;
|
||||
DocumentData *sticker = nullptr;
|
||||
@@ -162,16 +164,14 @@ private:
|
||||
int32 count = 0;
|
||||
QString title;
|
||||
int titleWidth = 0;
|
||||
bool installed = false;
|
||||
bool official = false;
|
||||
bool unread = false;
|
||||
bool archived = false;
|
||||
Data::StickersSetFlags flagsOverride;
|
||||
bool removed = false;
|
||||
int32 pixw = 0;
|
||||
int32 pixh = 0;
|
||||
anim::value yadd;
|
||||
std::unique_ptr<Ui::RippleAnimation> ripple;
|
||||
std::unique_ptr<Lottie::SinglePlayer> lottie;
|
||||
Media::Clip::ReaderPointer webm;
|
||||
};
|
||||
struct MegagroupSet {
|
||||
inline bool operator==(const MegagroupSet &other) const {
|
||||
@@ -221,16 +221,26 @@ private:
|
||||
void setActionSel(int32 actionSel);
|
||||
float64 aboveShadowOpacity() const;
|
||||
void validateLottieAnimation(not_null<Row*> row);
|
||||
void validateWebmAnimation(not_null<Row*> row);
|
||||
void validateAnimation(not_null<Row*> row);
|
||||
void updateRowThumbnail(not_null<Row*> row);
|
||||
|
||||
void clipCallback(
|
||||
not_null<Row*> row,
|
||||
Media::Clip::Notification notification);
|
||||
|
||||
void readVisibleSets();
|
||||
|
||||
void updateControlsGeometry();
|
||||
void rebuildAppendSet(not_null<StickersSet*> set, int maxNameWidth);
|
||||
void fillSetCover(not_null<StickersSet*> set, DocumentData **outSticker, int *outWidth, int *outHeight) const;
|
||||
int fillSetCount(not_null<StickersSet*> set) const;
|
||||
QString fillSetTitle(not_null<StickersSet*> set, int maxNameWidth, int *outTitleWidth) const;
|
||||
void fillSetFlags(not_null<StickersSet*> set, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outArchived);
|
||||
[[nodiscard]] QString fillSetTitle(
|
||||
not_null<StickersSet*> set,
|
||||
int maxNameWidth,
|
||||
int *outTitleWidth) const;
|
||||
[[nodiscard]] Data::StickersSetFlags fillSetFlags(
|
||||
not_null<StickersSet*> set) const;
|
||||
void rebuildMegagroupSet();
|
||||
void fixupMegagroupSetAddress();
|
||||
void handleMegagroupSetAddressChange();
|
||||
@@ -247,6 +257,7 @@ private:
|
||||
int32 _rowHeight;
|
||||
|
||||
std::vector<std::unique_ptr<Row>> _rows;
|
||||
std::vector<std::unique_ptr<Row>> _oldRows;
|
||||
std::vector<crl::time> _shiftingStartTimes;
|
||||
crl::time _aboveShadowFadeStart = 0;
|
||||
anim::value _aboveShadowFadeOpacity;
|
||||
@@ -322,19 +333,18 @@ void StickersBox::CounterWidget::setCounter(int counter) {
|
||||
auto dummy = QImage(1, 1, QImage::Format_ARGB32_Premultiplied);
|
||||
Painter p(&dummy);
|
||||
|
||||
auto newWidth = 0;
|
||||
Dialogs::Ui::paintUnreadCount(p, _text, 0, 0, _st, &newWidth);
|
||||
const auto badge = Dialogs::Ui::PaintUnreadBadge(p, _text, 0, 0, _st);
|
||||
|
||||
resize(newWidth, st::stickersFeaturedBadgeSize);
|
||||
resize(badge.width(), st::stickersFeaturedBadgeSize);
|
||||
}
|
||||
|
||||
void StickersBox::CounterWidget::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
if (!_text.isEmpty()) {
|
||||
auto unreadRight = rtl() ? 0 : width();
|
||||
auto unreadTop = 0;
|
||||
Dialogs::Ui::paintUnreadCount(p, _text, unreadRight, unreadTop, _st);
|
||||
const auto unreadRight = rtl() ? 0 : width();
|
||||
const auto unreadTop = 0;
|
||||
Dialogs::Ui::PaintUnreadBadge(p, _text, unreadRight, unreadTop, _st);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +557,9 @@ void StickersBox::prepare() {
|
||||
}
|
||||
setNoContentMargin(true);
|
||||
_tabs->sectionActivated(
|
||||
) | rpl::start_with_next(
|
||||
) | rpl::filter([=] {
|
||||
return !_ignoreTabActivation;
|
||||
}) | rpl::start_with_next(
|
||||
[this] { switchTab(); },
|
||||
lifetime());
|
||||
refreshTabs();
|
||||
@@ -665,12 +677,16 @@ void StickersBox::refreshTabs() {
|
||||
|| (_tab == &_featured && !_tabIndices.contains(Section::Featured))
|
||||
|| (_tab == &_masks && !_tabIndices.contains(Section::Masks))) {
|
||||
switchTab();
|
||||
} else if (_tab == &_archived) {
|
||||
_tabs->setActiveSectionFast(_tabIndices.indexOf(Section::Archived));
|
||||
} else if (_tab == &_featured) {
|
||||
_tabs->setActiveSectionFast(_tabIndices.indexOf(Section::Featured));
|
||||
} else if (_tab == &_masks) {
|
||||
_tabs->setActiveSectionFast(_tabIndices.indexOf(Section::Masks));
|
||||
} else {
|
||||
_ignoreTabActivation = true;
|
||||
_tabs->setActiveSectionFast(_tabIndices.indexOf((_tab == &_archived)
|
||||
? Section::Archived
|
||||
: (_tab == &_featured)
|
||||
? Section::Featured
|
||||
: (_tab == &_masks)
|
||||
? Section::Masks
|
||||
: Section::Installed));
|
||||
_ignoreTabActivation = false;
|
||||
}
|
||||
updateTabsGeometry();
|
||||
}
|
||||
@@ -1023,10 +1039,7 @@ StickersBox::Inner::Row::Row(
|
||||
int32 count,
|
||||
const QString &title,
|
||||
int titleWidth,
|
||||
bool installed,
|
||||
bool official,
|
||||
bool unread,
|
||||
bool archived,
|
||||
Data::StickersSetFlags flagsOverride,
|
||||
bool removed,
|
||||
int32 pixw,
|
||||
int32 pixh)
|
||||
@@ -1035,10 +1048,7 @@ StickersBox::Inner::Row::Row(
|
||||
, count(count)
|
||||
, title(title)
|
||||
, titleWidth(titleWidth)
|
||||
, installed(installed)
|
||||
, official(official)
|
||||
, unread(unread)
|
||||
, archived(archived)
|
||||
, flagsOverride(flagsOverride)
|
||||
, removed(removed)
|
||||
, pixw(pixw)
|
||||
, pixh(pixh) {
|
||||
@@ -1055,6 +1065,22 @@ bool StickersBox::Inner::Row::isMasksSet() const {
|
||||
return (set->flags & SetFlag::Masks);
|
||||
}
|
||||
|
||||
bool StickersBox::Inner::Row::isWebm() const {
|
||||
return (set->flags & SetFlag::Webm);
|
||||
}
|
||||
|
||||
bool StickersBox::Inner::Row::isInstalled() const {
|
||||
return (flagsOverride & SetFlag::Installed);
|
||||
}
|
||||
|
||||
bool StickersBox::Inner::Row::isUnread() const {
|
||||
return (flagsOverride & SetFlag::Unread);
|
||||
}
|
||||
|
||||
bool StickersBox::Inner::Row::isArchived() const {
|
||||
return (flagsOverride & SetFlag::Archived);
|
||||
}
|
||||
|
||||
StickersBox::Inner::Inner(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
@@ -1295,7 +1321,7 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> row, int index) {
|
||||
p.setPen(st::contactsNameFg);
|
||||
p.drawTextLeft(namex, namey, width(), row->title, row->titleWidth);
|
||||
|
||||
if (row->unread) {
|
||||
if (row->isUnread()) {
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::stickersFeaturedUnreadBg);
|
||||
|
||||
@@ -1337,22 +1363,17 @@ void StickersBox::Inner::paintRowThumbnail(
|
||||
row->stickerMedia->thumbnailWanted(origin);
|
||||
}
|
||||
}
|
||||
validateLottieAnimation(row);
|
||||
if (!row->lottie) {
|
||||
const auto thumb = row->thumbnailMedia
|
||||
? row->thumbnailMedia->image()
|
||||
: row->stickerMedia
|
||||
? row->stickerMedia->thumbnail()
|
||||
: nullptr;
|
||||
if (!thumb) {
|
||||
return;
|
||||
}
|
||||
p.drawPixmapLeft(
|
||||
left + (st::contactsPhotoSize - row->pixw) / 2,
|
||||
st::contactsPadding.top() + (st::contactsPhotoSize - row->pixh) / 2,
|
||||
width(),
|
||||
thumb->pix(row->pixw, row->pixh));
|
||||
} else if (row->lottie->ready()) {
|
||||
validateAnimation(row);
|
||||
const auto thumb = row->thumbnailMedia
|
||||
? row->thumbnailMedia->image()
|
||||
: row->stickerMedia
|
||||
? row->stickerMedia->thumbnail()
|
||||
: nullptr;
|
||||
const auto paused = _controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Layer);
|
||||
const auto x = left + (st::contactsPhotoSize - row->pixw) / 2;
|
||||
const auto y = st::contactsPadding.top() + (st::contactsPhotoSize - row->pixh) / 2;
|
||||
if (row->lottie && row->lottie->ready()) {
|
||||
const auto frame = row->lottie->frame();
|
||||
const auto size = frame.size() / cIntRetinaFactor();
|
||||
p.drawImage(
|
||||
@@ -1362,17 +1383,30 @@ void StickersBox::Inner::paintRowThumbnail(
|
||||
size.width(),
|
||||
size.height()),
|
||||
frame);
|
||||
const auto paused = _controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Layer);
|
||||
if (!paused) {
|
||||
row->lottie->markFrameShown();
|
||||
}
|
||||
} else if (row->webm && row->webm->started()) {
|
||||
p.drawPixmapLeft(
|
||||
x,
|
||||
y,
|
||||
width(),
|
||||
row->webm->current(
|
||||
{ .frame = { row->pixw, row->pixh }, .keepAlpha = true },
|
||||
paused ? 0 : crl::now()));
|
||||
} else if (thumb) {
|
||||
p.drawPixmapLeft(
|
||||
x,
|
||||
y,
|
||||
width(),
|
||||
thumb->pix(row->pixw, row->pixh));
|
||||
}
|
||||
}
|
||||
|
||||
void StickersBox::Inner::validateLottieAnimation(not_null<Row*> row) {
|
||||
if (row->lottie
|
||||
|| !ChatHelpers::HasLottieThumbnail(
|
||||
row->set->flags,
|
||||
row->thumbnailMedia.get(),
|
||||
row->stickerMedia.get())) {
|
||||
return;
|
||||
@@ -1394,6 +1428,51 @@ void StickersBox::Inner::validateLottieAnimation(not_null<Row*> row) {
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void StickersBox::Inner::validateWebmAnimation(not_null<Row*> row) {
|
||||
if (row->webm
|
||||
|| !ChatHelpers::HasWebmThumbnail(
|
||||
row->set->flags,
|
||||
row->thumbnailMedia.get(),
|
||||
row->stickerMedia.get())) {
|
||||
return;
|
||||
}
|
||||
auto callback = [=](Media::Clip::Notification notification) {
|
||||
clipCallback(row, notification);
|
||||
};
|
||||
row->webm = ChatHelpers::WebmThumbnail(
|
||||
row->thumbnailMedia.get(),
|
||||
row->stickerMedia.get(),
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
void StickersBox::Inner::clipCallback(
|
||||
not_null<Row*> row,
|
||||
Media::Clip::Notification notification) {
|
||||
using namespace Media::Clip;
|
||||
switch (notification) {
|
||||
case Notification::Reinit: {
|
||||
if (!row->webm) {
|
||||
return;
|
||||
} else if (row->webm->state() == State::Error) {
|
||||
row->webm.setBad();
|
||||
} else if (row->webm->ready() && !row->webm->started()) {
|
||||
row->webm->start({
|
||||
.frame = { row->pixw, row->pixh },
|
||||
.keepAlpha = true,
|
||||
});
|
||||
}
|
||||
} break;
|
||||
|
||||
case Notification::Repaint: break;
|
||||
}
|
||||
updateRowThumbnail(row);
|
||||
}
|
||||
|
||||
void StickersBox::Inner::validateAnimation(not_null<Row*> row) {
|
||||
validateWebmAnimation(row);
|
||||
validateLottieAnimation(row);
|
||||
}
|
||||
|
||||
void StickersBox::Inner::updateRowThumbnail(not_null<Row*> row) {
|
||||
const auto rowTop = [&] {
|
||||
if (row == _megagroupSelectedSet.get()) {
|
||||
@@ -1422,7 +1501,7 @@ void StickersBox::Inner::updateRowThumbnail(not_null<Row*> row) {
|
||||
void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int index) {
|
||||
auto removeButton = (_isInstalled && !row->removed);
|
||||
auto rect = relativeButtonRect(removeButton);
|
||||
if (!_isInstalled && row->installed && !row->archived && !row->removed) {
|
||||
if (!_isInstalled && row->isInstalled() && !row->isArchived() && !row->removed) {
|
||||
// Checkbox after installed from Trending or Archived.
|
||||
int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (rect.width() + st::stickersFeaturedInstalled.width()) / 2);
|
||||
int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2;
|
||||
@@ -1509,7 +1588,7 @@ void StickersBox::Inner::setActionDown(int newActionDown) {
|
||||
auto rippleMask = Ui::RippleAnimation::ellipseMask(QSize(rippleSize, rippleSize));
|
||||
ensureRipple(st::stickersRemove.ripple, std::move(rippleMask), removeButton);
|
||||
}
|
||||
} else if (!row->installed || row->archived || row->removed) {
|
||||
} else if (!row->isInstalled() || row->isArchived() || row->removed) {
|
||||
auto rippleSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height);
|
||||
auto rippleMask = Ui::RippleAnimation::roundRectMask(rippleSize, st::roundRadiusSmall);
|
||||
ensureRipple(st::stickersTrendingAdd.ripple, std::move(rippleMask), removeButton);
|
||||
@@ -1642,7 +1721,7 @@ void StickersBox::Inner::updateSelected() {
|
||||
selected = selectedIndex;
|
||||
local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight);
|
||||
const auto row = _rows[selectedIndex].get();
|
||||
if (!_megagroupSet && (_isInstalled || !row->installed || row->archived || row->removed)) {
|
||||
if (!_megagroupSet && (_isInstalled || !row->isInstalled() || row->isArchived() || row->removed)) {
|
||||
auto removeButton = (_isInstalled && !row->removed);
|
||||
auto rect = myrtlrect(relativeButtonRect(removeButton));
|
||||
actionSel = rect.contains(local) ? selectedIndex : -1;
|
||||
@@ -1945,8 +2024,10 @@ void StickersBox::Inner::rebuildMegagroupSet() {
|
||||
auto sticker = (DocumentData*)nullptr;
|
||||
auto pixw = 0, pixh = 0;
|
||||
fillSetCover(set, &sticker, &pixw, &pixh);
|
||||
auto installed = true, official = false, unread = false, archived = false, removed = false;
|
||||
if (!_megagroupSelectedSet || _megagroupSelectedSet->set->id != set->id) {
|
||||
auto flagsOverride = SetFlag::Installed;
|
||||
auto removed = false;
|
||||
if (!_megagroupSelectedSet
|
||||
|| _megagroupSelectedSet->set->id != set->id) {
|
||||
_megagroupSetField->setText(set->shortName);
|
||||
_megagroupSetField->finishAnimating();
|
||||
}
|
||||
@@ -1956,10 +2037,7 @@ void StickersBox::Inner::rebuildMegagroupSet() {
|
||||
count,
|
||||
title,
|
||||
titleWidth,
|
||||
installed,
|
||||
official,
|
||||
unread,
|
||||
archived,
|
||||
flagsOverride,
|
||||
removed,
|
||||
pixw,
|
||||
pixh);
|
||||
@@ -1987,6 +2065,7 @@ void StickersBox::Inner::rebuild(bool masks) {
|
||||
|
||||
auto maxNameWidth = countMaxNameWidth();
|
||||
|
||||
_oldRows = std::move(_rows);
|
||||
clear();
|
||||
const auto &order = ([&]() -> const StickersSetsOrder & {
|
||||
if (_section == Section::Installed) {
|
||||
@@ -2038,6 +2117,7 @@ void StickersBox::Inner::rebuild(bool masks) {
|
||||
set->accessHash);
|
||||
}
|
||||
}
|
||||
_oldRows.clear();
|
||||
session().api().requestStickerSets();
|
||||
updateSize();
|
||||
}
|
||||
@@ -2086,13 +2166,14 @@ void StickersBox::Inner::updateRows() {
|
||||
}
|
||||
}
|
||||
if (!row->isRecentSet()) {
|
||||
auto wasInstalled = row->installed;
|
||||
auto wasArchived = row->archived;
|
||||
fillSetFlags(set, &row->installed, &row->official, &row->unread, &row->archived);
|
||||
auto wasInstalled = row->isInstalled();
|
||||
auto wasArchived = row->isArchived();
|
||||
row->flagsOverride = fillSetFlags(set);
|
||||
if (_isInstalled) {
|
||||
row->archived = false;
|
||||
row->flagsOverride &= ~SetFlag::Archived;
|
||||
}
|
||||
if (row->installed != wasInstalled || row->archived != wasArchived) {
|
||||
if (row->isInstalled() != wasInstalled
|
||||
|| row->isArchived() != wasArchived) {
|
||||
row->ripple.reset();
|
||||
}
|
||||
}
|
||||
@@ -2134,11 +2215,11 @@ int StickersBox::Inner::countMaxNameWidth() const {
|
||||
void StickersBox::Inner::rebuildAppendSet(
|
||||
not_null<StickersSet*> set,
|
||||
int maxNameWidth) {
|
||||
bool installed = true, official = true, unread = false, archived = false, removed = false;
|
||||
if (set->id != Data::Stickers::CloudRecentSetId) {
|
||||
fillSetFlags(set, &installed, &official, &unread, &archived);
|
||||
}
|
||||
if (_isInstalled && archived) {
|
||||
auto flagsOverride = (set->id != Data::Stickers::CloudRecentSetId)
|
||||
? fillSetFlags(set)
|
||||
: SetFlag::Installed;
|
||||
auto removed = false;
|
||||
if (_isInstalled && (flagsOverride & SetFlag::Archived)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2150,19 +2231,49 @@ void StickersBox::Inner::rebuildAppendSet(
|
||||
QString title = fillSetTitle(set, maxNameWidth, &titleWidth);
|
||||
int count = fillSetCount(set);
|
||||
|
||||
_rows.push_back(std::make_unique<Row>(
|
||||
set,
|
||||
sticker,
|
||||
count,
|
||||
title,
|
||||
titleWidth,
|
||||
installed,
|
||||
official,
|
||||
unread,
|
||||
archived,
|
||||
removed,
|
||||
pixw,
|
||||
pixh));
|
||||
const auto existing = [&]{
|
||||
const auto now = int(_rows.size());
|
||||
const auto setProj = [](const std::unique_ptr<Row> &row) {
|
||||
return row ? row->set.get() : nullptr;
|
||||
};
|
||||
if (_oldRows.size() > now
|
||||
&& setProj(_oldRows[now]) == set.get()) {
|
||||
return _oldRows.begin() + now;
|
||||
}
|
||||
return ranges::find(_oldRows, set.get(), setProj);
|
||||
}();
|
||||
if (existing != end(_oldRows)) {
|
||||
const auto raw = existing->get();
|
||||
raw->sticker = sticker;
|
||||
raw->count = count;
|
||||
raw->title = title;
|
||||
raw->titleWidth = titleWidth;
|
||||
raw->flagsOverride = flagsOverride;
|
||||
raw->removed = removed;
|
||||
raw->pixw = pixw;
|
||||
raw->pixh = pixh;
|
||||
raw->yadd = {};
|
||||
auto oldStickerMedia = std::move(raw->stickerMedia);
|
||||
auto oldThumbnailMedia = std::move(raw->thumbnailMedia);
|
||||
raw->stickerMedia = sticker ? sticker->activeMediaView() : nullptr;
|
||||
raw->thumbnailMedia = set->activeThumbnailView();
|
||||
if (raw->thumbnailMedia != oldThumbnailMedia
|
||||
|| (!raw->thumbnailMedia && raw->stickerMedia != oldStickerMedia)) {
|
||||
raw->lottie = nullptr;
|
||||
}
|
||||
_rows.push_back(std::move(*existing));
|
||||
} else {
|
||||
_rows.push_back(std::make_unique<Row>(
|
||||
set,
|
||||
sticker,
|
||||
count,
|
||||
title,
|
||||
titleWidth,
|
||||
flagsOverride,
|
||||
removed,
|
||||
pixw,
|
||||
pixh));
|
||||
}
|
||||
_shiftingStartTimes.push_back(0);
|
||||
}
|
||||
|
||||
@@ -2244,20 +2355,12 @@ QString StickersBox::Inner::fillSetTitle(
|
||||
return result;
|
||||
}
|
||||
|
||||
void StickersBox::Inner::fillSetFlags(
|
||||
not_null<StickersSet*> set,
|
||||
bool *outInstalled,
|
||||
bool *outOfficial,
|
||||
bool *outUnread,
|
||||
bool *outArchived) {
|
||||
*outInstalled = (set->flags & SetFlag::Installed);
|
||||
*outOfficial = (set->flags & SetFlag::Official);
|
||||
*outArchived = (set->flags & SetFlag::Archived);
|
||||
if (_section == Section::Featured) {
|
||||
*outUnread = (set->flags & SetFlag::Unread);
|
||||
} else {
|
||||
*outUnread = false;
|
||||
}
|
||||
Data::StickersSetFlags StickersBox::Inner::fillSetFlags(
|
||||
not_null<StickersSet*> set) const {
|
||||
const auto result = set->flags;
|
||||
return (_section == Section::Featured)
|
||||
? result
|
||||
: (result & ~SetFlag::Unread);
|
||||
}
|
||||
|
||||
template <typename Check>
|
||||
@@ -2274,7 +2377,7 @@ StickersSetsOrder StickersBox::Inner::collectSets(Check check) const {
|
||||
|
||||
StickersSetsOrder StickersBox::Inner::getOrder() const {
|
||||
return collectSets([](Row *row) {
|
||||
return !row->archived && !row->removed && !row->isRecentSet();
|
||||
return !row->isArchived() && !row->removed && !row->isRecentSet();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2349,7 +2452,7 @@ void StickersBox::Inner::readVisibleSets() {
|
||||
int rowTo = ceilclamp(itemsVisibleBottom, _rowHeight, 0, _rows.size());
|
||||
for (int i = rowFrom; i < rowTo; ++i) {
|
||||
const auto row = _rows[i].get();
|
||||
if (!row->unread) {
|
||||
if (!row->isUnread()) {
|
||||
continue;
|
||||
}
|
||||
if ((i * _rowHeight < itemsVisibleTop)
|
||||
|
||||
@@ -143,6 +143,7 @@ private:
|
||||
|
||||
object_ptr<Ui::SettingsSlider> _tabs = { nullptr };
|
||||
QList<Section> _tabIndices;
|
||||
bool _ignoreTabActivation = false;
|
||||
|
||||
class CounterWidget;
|
||||
object_ptr<CounterWidget> _unreadBadge = { nullptr };
|
||||
|
||||
@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
@@ -255,11 +256,11 @@ not_null<Ui::RpWidget*> UrlAuthBox::setupContent(
|
||||
tr::lng_url_auth_open_confirm(tr::now, lt_link, url),
|
||||
st::boxLabel),
|
||||
st::boxPadding);
|
||||
const auto addCheckbox = [&](const QString &text) {
|
||||
const auto addCheckbox = [&](const TextWithEntities &text) {
|
||||
const auto checkbox = result->add(
|
||||
object_ptr<Ui::Checkbox>(
|
||||
result,
|
||||
QString(),
|
||||
text,
|
||||
true,
|
||||
st::urlAuthCheckbox),
|
||||
style::margins(
|
||||
@@ -268,23 +269,22 @@ not_null<Ui::RpWidget*> UrlAuthBox::setupContent(
|
||||
st::boxPadding.right(),
|
||||
st::boxPadding.bottom()));
|
||||
checkbox->setAllowTextLines();
|
||||
checkbox->setText(text, true);
|
||||
return checkbox;
|
||||
};
|
||||
const auto auth = addCheckbox(
|
||||
tr::lng_url_auth_login_option(
|
||||
tr::now,
|
||||
lt_domain,
|
||||
textcmdStartSemibold() + domain + textcmdStopSemibold(),
|
||||
Ui::Text::Bold(domain),
|
||||
lt_user,
|
||||
(textcmdStartSemibold()
|
||||
+ session->user()->name
|
||||
+ textcmdStopSemibold())));
|
||||
Ui::Text::Bold(session->user()->name),
|
||||
Ui::Text::WithEntities));
|
||||
const auto allow = bot
|
||||
? addCheckbox(tr::lng_url_auth_allow_messages(
|
||||
tr::now,
|
||||
lt_bot,
|
||||
textcmdStartSemibold() + bot->firstName + textcmdStopSemibold()))
|
||||
Ui::Text::Bold(bot->firstName),
|
||||
Ui::Text::WithEntities))
|
||||
: nullptr;
|
||||
if (allow) {
|
||||
rpl::single(
|
||||
|
||||
@@ -411,49 +411,45 @@ callBarSignalBars: CallSignalBars(callPanelSignalBars) {
|
||||
color: callBarFg;
|
||||
}
|
||||
|
||||
callTitleButton: IconButton {
|
||||
width: 34px;
|
||||
height: 30px;
|
||||
iconPosition: point(0px, 0px);
|
||||
}
|
||||
callTitleButton: windowTitleButton;
|
||||
callTitleMinimizeIcon: icon {
|
||||
{ "calls/calls_minimize_shadow", windowShadowFg },
|
||||
{ "calls/calls_minimize_main", callNameFg },
|
||||
{ "title_shadow_minimize", windowShadowFg },
|
||||
{ "title_button_minimize", callNameFg },
|
||||
};
|
||||
callTitleMinimizeIconOver: icon {
|
||||
{ size(34px, 30px), callBgButton },
|
||||
{ size(34px, 30px), callMuteRipple },
|
||||
{ "calls/calls_minimize_shadow", windowShadowFg },
|
||||
{ "calls/calls_minimize_main", callNameFg },
|
||||
{ windowTitleButtonSize, callBgButton },
|
||||
{ windowTitleButtonSize, callMuteRipple },
|
||||
{ "title_shadow_minimize", windowShadowFg },
|
||||
{ "title_button_minimize", callNameFg },
|
||||
};
|
||||
callTitleMaximizeIcon: icon {
|
||||
{ "calls/calls_maximize_shadow", windowShadowFg },
|
||||
{ "calls/calls_maximize_main", callNameFg },
|
||||
{ "title_shadow_maximize", windowShadowFg },
|
||||
{ "title_button_maximize", callNameFg },
|
||||
};
|
||||
callTitleMaximizeIconOver: icon {
|
||||
{ size(34px, 30px), callBgButton },
|
||||
{ size(34px, 30px), callMuteRipple },
|
||||
{ "calls/calls_maximize_shadow", windowShadowFg },
|
||||
{ "calls/calls_maximize_main", callNameFg },
|
||||
{ windowTitleButtonSize, callBgButton },
|
||||
{ windowTitleButtonSize, callMuteRipple },
|
||||
{ "title_shadow_maximize", windowShadowFg },
|
||||
{ "title_button_maximize", callNameFg },
|
||||
};
|
||||
callTitleRestoreIcon: icon {
|
||||
{ "calls/calls_restore_shadow", windowShadowFg },
|
||||
{ "calls/calls_restore_main", callNameFg },
|
||||
{ "title_shadow_restore", windowShadowFg },
|
||||
{ "title_button_restore", callNameFg },
|
||||
};
|
||||
callTitleRestoreIconOver: icon {
|
||||
{ size(34px, 30px), callBgButton },
|
||||
{ size(34px, 30px), callMuteRipple },
|
||||
{ "calls/calls_restore_shadow", windowShadowFg },
|
||||
{ "calls/calls_restore_main", callNameFg },
|
||||
{ windowTitleButtonSize, callBgButton },
|
||||
{ windowTitleButtonSize, callMuteRipple },
|
||||
{ "title_shadow_restore", windowShadowFg },
|
||||
{ "title_button_restore", callNameFg },
|
||||
};
|
||||
callTitleCloseIcon: icon {
|
||||
{ "calls/calls_close_shadow", windowShadowFg },
|
||||
{ "calls/calls_close_main", callNameFg },
|
||||
{ "title_shadow_close", windowShadowFg },
|
||||
{ "title_button_close", callNameFg },
|
||||
};
|
||||
callTitleCloseIconOver: icon {
|
||||
{ size(34px, 30px), titleButtonCloseBgOver },
|
||||
{ "calls/calls_close_shadow", windowShadowFg },
|
||||
{ "calls/calls_close_main", titleButtonCloseFgOver },
|
||||
{ windowTitleButtonSize, titleButtonCloseBgOver },
|
||||
{ "title_shadow_close", windowShadowFg },
|
||||
{ "title_button_close", titleButtonCloseFgOver },
|
||||
};
|
||||
callTitle: WindowTitle(defaultWindowTitle) {
|
||||
height: 0px;
|
||||
@@ -1055,37 +1051,37 @@ groupCallDelaySlider: MediaSlider(defaultContinuousSlider) {
|
||||
groupCallDelayMargin: margins(22px, 5px, 20px, 10px);
|
||||
|
||||
groupCallTitleButton: IconButton {
|
||||
width: 24px;
|
||||
height: 21px;
|
||||
width: windowTitleButtonWidth;
|
||||
height: windowTitleHeight;
|
||||
iconPosition: point(0px, 0px);
|
||||
}
|
||||
groupCallTitleMinimizeIcon: icon {
|
||||
{ "title_button_minimize", groupCallMemberNotJoinedStatus, point(4px, 4px) },
|
||||
{ "title_button_minimize", groupCallMemberNotJoinedStatus },
|
||||
};
|
||||
groupCallTitleMinimizeIconOver: icon {
|
||||
{ size(24px, 21px), groupCallMembersBgOver },
|
||||
{ "title_button_minimize", groupCallMembersFg, point(4px, 4px) },
|
||||
{ windowTitleButtonSize, groupCallMembersBgOver },
|
||||
{ "title_button_minimize", groupCallMembersFg },
|
||||
};
|
||||
groupCallTitleMaximizeIcon: icon {
|
||||
{ "title_button_maximize", groupCallMemberNotJoinedStatus, point(4px, 4px) },
|
||||
{ "title_button_maximize", groupCallMemberNotJoinedStatus },
|
||||
};
|
||||
groupCallTitleMaximizeIconOver: icon {
|
||||
{ size(24px, 21px), groupCallMembersBgOver },
|
||||
{ "title_button_maximize", groupCallMembersFg, point(4px, 4px) },
|
||||
{ windowTitleButtonSize, groupCallMembersBgOver },
|
||||
{ "title_button_maximize", groupCallMembersFg },
|
||||
};
|
||||
groupCallTitleRestoreIcon: icon {
|
||||
{ "title_button_restore", groupCallMemberNotJoinedStatus, point(4px, 4px) },
|
||||
{ "title_button_restore", groupCallMemberNotJoinedStatus },
|
||||
};
|
||||
groupCallTitleRestoreIconOver: icon {
|
||||
{ size(24px, 21px), groupCallMembersBgOver },
|
||||
{ "title_button_restore", groupCallMembersFg, point(4px, 4px) },
|
||||
{ windowTitleButtonSize, groupCallMembersBgOver },
|
||||
{ "title_button_restore", groupCallMembersFg },
|
||||
};
|
||||
groupCallTitleCloseIcon: icon {
|
||||
{ "title_button_close", groupCallMemberNotJoinedStatus, point(4px, 4px) },
|
||||
{ "title_button_close", groupCallMemberNotJoinedStatus },
|
||||
};
|
||||
groupCallTitleCloseIconOver: icon {
|
||||
{ size(24px, 21px), titleButtonCloseBgOver },
|
||||
{ "title_button_close", titleButtonCloseFgOver, point(4px, 4px) },
|
||||
{ windowTitleButtonSize, titleButtonCloseBgOver },
|
||||
{ "title_button_close", titleButtonCloseFgOver },
|
||||
};
|
||||
groupCallTitle: WindowTitle(defaultWindowTitle) {
|
||||
height: 0px;
|
||||
@@ -1199,7 +1195,7 @@ desktopCaptureSourceSkips: size(2px, 10px);
|
||||
desktopCaptureSourceTitle: WindowTitle(groupCallTitle) {
|
||||
bg: groupCallMembersBgOver;
|
||||
bgActive: groupCallMembersBgOver;
|
||||
height: 21px;
|
||||
height: windowTitleHeight;
|
||||
}
|
||||
desktopCapturePadding: margins(7px, 7px, 7px, 33px);
|
||||
desktopCaptureLabelBottom: 7px;
|
||||
|
||||
@@ -162,14 +162,13 @@ Call::Call(
|
||||
, _user(user)
|
||||
, _api(&_user->session().mtp())
|
||||
, _type(type)
|
||||
, _discardByTimeoutTimer([=] { hangup(); })
|
||||
, _videoIncoming(
|
||||
std::make_unique<Webrtc::VideoTrack>(
|
||||
StartVideoState(video)))
|
||||
, _videoOutgoing(
|
||||
std::make_unique<Webrtc::VideoTrack>(
|
||||
StartVideoState(video))) {
|
||||
_discardByTimeoutTimer.setCallback([=] { hangup(); });
|
||||
|
||||
if (_type == Type::Outgoing) {
|
||||
setState(State::Requesting);
|
||||
} else {
|
||||
|
||||
@@ -32,7 +32,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/toast/toast.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "app.h" // App::quitting
|
||||
|
||||
#include <tgcalls/VideoCaptureInterface.h>
|
||||
#include <tgcalls/StaticThreads.h>
|
||||
@@ -247,7 +246,7 @@ void Instance::destroyCall(not_null<Call*> call) {
|
||||
_currentCallChanges.fire(nullptr);
|
||||
taken.reset();
|
||||
|
||||
if (App::quitting()) {
|
||||
if (Core::Quitting()) {
|
||||
LOG(("Calls::Instance doesn't prevent quit any more."));
|
||||
}
|
||||
Core::App().quitPreventFinished();
|
||||
@@ -285,7 +284,7 @@ void Instance::destroyGroupCall(not_null<GroupCall*> call) {
|
||||
_currentGroupCallChanges.fire(nullptr);
|
||||
taken.reset();
|
||||
|
||||
if (App::quitting()) {
|
||||
if (Core::Quitting()) {
|
||||
LOG(("Calls::Instance doesn't prevent quit any more."));
|
||||
}
|
||||
Core::App().quitPreventFinished();
|
||||
|
||||
@@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "apiwrap.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "base/power_save_blocker.h"
|
||||
#include "window/main_window.h"
|
||||
#include "webrtc/webrtc_video_track.h"
|
||||
#include "webrtc/webrtc_media_devices.h"
|
||||
@@ -61,8 +62,8 @@ Panel::Panel(not_null<Call*> call)
|
||||
, _user(call->user())
|
||||
, _layerBg(std::make_unique<Ui::LayerManager>(widget()))
|
||||
#ifndef Q_OS_MAC
|
||||
, _controls(std::make_unique<Ui::Platform::TitleControls>(
|
||||
widget(),
|
||||
, _controls(Ui::Platform::SetupSeparateTitleControls(
|
||||
window(),
|
||||
st::callTitle,
|
||||
[=](bool maximized) { toggleFullScreen(maximized); }))
|
||||
#endif // !Q_OS_MAC
|
||||
@@ -144,7 +145,7 @@ void Panel::initWindow() {
|
||||
return Flag::None | Flag(0);
|
||||
}
|
||||
#ifndef Q_OS_MAC
|
||||
if (_controls->geometry().contains(widgetPoint)) {
|
||||
if (_controls->controls.geometry().contains(widgetPoint)) {
|
||||
return Flag::None | Flag(0);
|
||||
}
|
||||
#endif // !Q_OS_MAC
|
||||
@@ -357,6 +358,7 @@ void Panel::reinitWithCall(Call *call) {
|
||||
if (!_call) {
|
||||
_incoming = nullptr;
|
||||
_outgoingVideoBubble = nullptr;
|
||||
_powerSaveBlocker = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -497,6 +499,11 @@ void Panel::reinitWithCall(Call *call) {
|
||||
_camera->raise();
|
||||
_mute->raise();
|
||||
|
||||
_powerSaveBlocker = std::make_unique<base::PowerSaveBlocker>(
|
||||
base::PowerSaveBlockType::PreventDisplaySleep,
|
||||
u"Video call is active"_q,
|
||||
window()->windowHandle());
|
||||
|
||||
_incoming->widget()->lower();
|
||||
}
|
||||
|
||||
@@ -550,7 +557,7 @@ void Panel::initLayout() {
|
||||
}, widget()->lifetime());
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
_controls->raise();
|
||||
_controls->wrap.raise();
|
||||
#endif // !Q_OS_MAC
|
||||
}
|
||||
|
||||
@@ -628,7 +635,7 @@ void Panel::updateControlsGeometry() {
|
||||
}
|
||||
if (_fingerprint) {
|
||||
#ifndef Q_OS_MAC
|
||||
const auto controlsGeometry = _controls->geometry();
|
||||
const auto controlsGeometry = _controls->controls.geometry();
|
||||
const auto halfWidth = widget()->width() / 2;
|
||||
const auto minLeft = (controlsGeometry.center().x() < halfWidth)
|
||||
? (controlsGeometry.width() + st::callFingerprintTop)
|
||||
@@ -804,6 +811,10 @@ void Panel::stateChanged(State state) {
|
||||
&& (state != State::EndedByOtherDevice)
|
||||
&& (state != State::FailedHangingUp)
|
||||
&& (state != State::Failed)) {
|
||||
if (state == State::Busy) {
|
||||
_powerSaveBlocker = nullptr;
|
||||
}
|
||||
|
||||
auto toggleButton = [&](auto &&button, bool visible) {
|
||||
button->toggle(
|
||||
visible,
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/weak_ptr.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/object_ptr.h"
|
||||
#include "base/unique_qptr.h"
|
||||
#include "calls/calls_call.h"
|
||||
#include "calls/group/ui/desktop_capture_choose_source.h"
|
||||
#include "ui/effects/animations.h"
|
||||
@@ -18,6 +19,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class Image;
|
||||
|
||||
namespace base {
|
||||
class PowerSaveBlocker;
|
||||
} // namespace base
|
||||
|
||||
namespace Data {
|
||||
class PhotoMedia;
|
||||
class CloudImageView;
|
||||
@@ -37,7 +42,7 @@ namespace GL {
|
||||
enum class Backend;
|
||||
} // namespace GL
|
||||
namespace Platform {
|
||||
class TitleControls;
|
||||
struct SeparateTitleControls;
|
||||
} // namespace Platform
|
||||
} // namespace Ui
|
||||
|
||||
@@ -126,9 +131,11 @@ private:
|
||||
std::unique_ptr<Incoming> _incoming;
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
std::unique_ptr<Ui::Platform::TitleControls> _controls;
|
||||
std::unique_ptr<Ui::Platform::SeparateTitleControls> _controls;
|
||||
#endif // !Q_OS_MAC
|
||||
|
||||
std::unique_ptr<base::PowerSaveBlocker> _powerSaveBlocker;
|
||||
|
||||
QSize _incomingFrameSize;
|
||||
|
||||
rpl::lifetime _callLifetime;
|
||||
|
||||
@@ -165,8 +165,11 @@ void Userpic::refreshPhoto() {
|
||||
void Userpic::createCache(Image *image) {
|
||||
const auto size = this->size();
|
||||
const auto real = size * cIntRetinaFactor();
|
||||
auto options = Images::Option::Smooth | Images::Option::Circled;
|
||||
// _useTransparency ? (Images::Option::RoundedLarge | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::Smooth) : Images::Option::None;
|
||||
//_useTransparency
|
||||
// ? (Images::Option::RoundLarge
|
||||
// | Images::Option::RoundSkipBottomLeft
|
||||
// | Images::Option::RoundSkipBottomRight)
|
||||
// : Images::Option::None;
|
||||
if (image) {
|
||||
auto width = image->width();
|
||||
auto height = image->height();
|
||||
@@ -178,14 +181,16 @@ void Userpic::createCache(Image *image) {
|
||||
width = real;
|
||||
}
|
||||
_userPhoto = image->pixNoCache(
|
||||
width,
|
||||
height,
|
||||
options,
|
||||
size,
|
||||
size);
|
||||
{ width, height },
|
||||
{
|
||||
.options = Images::Option::RoundCircle,
|
||||
.outer = { size, size },
|
||||
});
|
||||
_userPhoto.setDevicePixelRatio(cRetinaFactor());
|
||||
} else {
|
||||
auto filled = QImage(QSize(real, real), QImage::Format_ARGB32_Premultiplied);
|
||||
auto filled = QImage(
|
||||
QSize(real, real),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
filled.setDevicePixelRatio(cRetinaFactor());
|
||||
filled.fill(Qt::transparent);
|
||||
{
|
||||
@@ -195,7 +200,10 @@ void Userpic::createCache(Image *image) {
|
||||
_peer->name
|
||||
).paint(p, 0, 0, size, size);
|
||||
}
|
||||
//Images::prepareRound(filled, ImageRoundRadius::Large, RectPart::TopLeft | RectPart::TopRight);
|
||||
//_userPhoto = Images::PixmapFast(Images::Round(
|
||||
// std::move(filled),
|
||||
// ImageRoundRadius::Large,
|
||||
// RectPart::TopLeft | RectPart::TopRight));
|
||||
_userPhoto = Images::PixmapFast(std::move(filled));
|
||||
}
|
||||
|
||||
|
||||
@@ -169,12 +169,12 @@ void VideoBubble::prepareFrame() {
|
||||
for (; from != till; from += fromPerLine, to += toPerLine) {
|
||||
memcpy(to, from, lineSize);
|
||||
}
|
||||
Images::prepareRound(
|
||||
_frame,
|
||||
_frame = Images::Round(
|
||||
std::move(_frame),
|
||||
ImageRoundRadius::Large,
|
||||
RectPart::AllCorners,
|
||||
QRect(QPoint(), size));
|
||||
_frame = std::move(_frame).mirrored(true, false);
|
||||
QRect(QPoint(), size)
|
||||
).mirrored(true, false);
|
||||
}
|
||||
|
||||
void VideoBubble::setState(Webrtc::VideoState state) {
|
||||
|
||||
@@ -2396,6 +2396,7 @@ bool GroupCall::tryCreateScreencast() {
|
||||
tgcalls::GroupInstanceDescriptor descriptor = {
|
||||
.threads = tgcalls::StaticThreads::getThreads(),
|
||||
.config = tgcalls::GroupConfig{
|
||||
.need_log = Logs::DebugEnabled(),
|
||||
},
|
||||
.networkStateUpdated = [=](tgcalls::GroupNetworkState networkState) {
|
||||
crl::on_main(weak, [=] {
|
||||
|
||||
@@ -1195,7 +1195,10 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
|
||||
const auto admin = IsGroupCallAdmin(_peer, participantPeer);
|
||||
const auto session = &_peer->session();
|
||||
const auto getCurrentWindow = [=]() -> Window::SessionController* {
|
||||
if (const auto window = Core::App().activeWindow()) {
|
||||
if (const auto window = Core::App().separateWindowForPeer(
|
||||
participantPeer)) {
|
||||
return window->sessionController();
|
||||
} else if (const auto window = Core::App().activeWindow()) {
|
||||
if (const auto controller = window->sessionController()) {
|
||||
if (&controller->session() == session) {
|
||||
return controller;
|
||||
@@ -1221,7 +1224,7 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
|
||||
? st::groupCallPopupMenuWithVolume
|
||||
: st::groupCallPopupMenu));
|
||||
const auto weakMenu = Ui::MakeWeak(result.get());
|
||||
const auto performOnMainWindow = [=](auto callback) {
|
||||
const auto withActiveWindow = [=](auto callback) {
|
||||
if (const auto window = getWindow()) {
|
||||
if (const auto menu = weakMenu.data()) {
|
||||
menu->discardParentReActivate();
|
||||
@@ -1236,12 +1239,12 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
|
||||
}
|
||||
};
|
||||
const auto showProfile = [=] {
|
||||
performOnMainWindow([=](not_null<Window::SessionController*> window) {
|
||||
withActiveWindow([=](not_null<Window::SessionController*> window) {
|
||||
window->showPeerInfo(participantPeer);
|
||||
});
|
||||
};
|
||||
const auto showHistory = [=] {
|
||||
performOnMainWindow([=](not_null<Window::SessionController*> window) {
|
||||
withActiveWindow([=](not_null<Window::SessionController*> window) {
|
||||
window->showPeerHistory(
|
||||
participantPeer,
|
||||
Window::SectionShow::Way::Forward);
|
||||
|
||||
@@ -108,7 +108,7 @@ private:
|
||||
};
|
||||
|
||||
TextParseOptions MenuTextOptions = {
|
||||
TextParseLinks | TextParseRichText, // flags
|
||||
TextParseLinks, // flags
|
||||
0, // maxw
|
||||
0, // maxh
|
||||
Qt::LayoutDirectionAuto, // dir
|
||||
|
||||
@@ -49,6 +49,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/unixtime.h"
|
||||
#include "base/qt_signal_producer.h"
|
||||
#include "base/timer_rpl.h"
|
||||
#include "base/power_save_blocker.h"
|
||||
#include "apiwrap.h" // api().kick.
|
||||
#include "api/api_chat_participants.h" // api().kick.
|
||||
#include "webrtc/webrtc_video_track.h"
|
||||
@@ -88,10 +89,14 @@ Panel::Panel(not_null<GroupCall*> call)
|
||||
, _peer(call->peer())
|
||||
, _layerBg(std::make_unique<Ui::LayerManager>(widget()))
|
||||
#ifndef Q_OS_MAC
|
||||
, _controls(std::make_unique<Ui::Platform::TitleControls>(
|
||||
widget(),
|
||||
, _controls(Ui::Platform::SetupSeparateTitleControls(
|
||||
window(),
|
||||
st::groupCallTitle))
|
||||
#endif // !Q_OS_MAC
|
||||
, _powerSaveBlocker(std::make_unique<base::PowerSaveBlocker>(
|
||||
base::PowerSaveBlockType::PreventDisplaySleep,
|
||||
u"Video chat is active"_q,
|
||||
window()->windowHandle()))
|
||||
, _viewport(
|
||||
std::make_unique<Viewport>(widget(), PanelMode::Wide, _window.backend()))
|
||||
, _mute(std::make_unique<Ui::CallMuteButton>(
|
||||
@@ -302,9 +307,9 @@ void Panel::initWidget() {
|
||||
updateControlsGeometry();
|
||||
}
|
||||
|
||||
// title geometry depends on _controls->geometry,
|
||||
// some geometries depends on _controls->controls.geometry,
|
||||
// which is not updated here yet.
|
||||
crl::on_main(widget(), [=] { refreshTitle(); });
|
||||
crl::on_main(widget(), [=] { updateControlsGeometry(); });
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
@@ -1368,7 +1373,7 @@ void Panel::initLayout() {
|
||||
initGeometry();
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
_controls->raise();
|
||||
_controls->wrap.raise();
|
||||
|
||||
Ui::Platform::TitleControlsLayoutChanged(
|
||||
) | rpl::start_with_next([=] {
|
||||
@@ -1413,7 +1418,7 @@ QRect Panel::computeTitleRect() const {
|
||||
#ifdef Q_OS_MAC
|
||||
return QRect(70, 0, width - remove - 70, 28);
|
||||
#else // Q_OS_MAC
|
||||
const auto controls = _controls->geometry();
|
||||
const auto controls = _controls->controls.geometry();
|
||||
const auto right = controls.x() + controls.width() + skip;
|
||||
return (controls.center().x() < width / 2)
|
||||
? QRect(right, 0, width - right - remove, controls.height())
|
||||
@@ -1884,7 +1889,8 @@ void Panel::updateControlsGeometry() {
|
||||
#ifdef Q_OS_MAC
|
||||
const auto controlsOnTheLeft = true;
|
||||
#else // Q_OS_MAC
|
||||
const auto controlsOnTheLeft = _controls->geometry().center().x()
|
||||
const auto center = _controls->controls.geometry().center();
|
||||
const auto controlsOnTheLeft = center.x()
|
||||
< widget()->width() / 2;
|
||||
#endif // Q_OS_MAC
|
||||
const auto menux = st::groupCallMenuTogglePosition.x();
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/timer.h"
|
||||
#include "base/flags.h"
|
||||
#include "base/object_ptr.h"
|
||||
#include "base/unique_qptr.h"
|
||||
#include "calls/group/calls_group_call.h"
|
||||
#include "calls/group/calls_group_common.h"
|
||||
#include "calls/group/calls_choose_join_as.h"
|
||||
@@ -21,6 +22,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class Image;
|
||||
|
||||
namespace base {
|
||||
class PowerSaveBlocker;
|
||||
} // namespace base
|
||||
|
||||
namespace Data {
|
||||
class PhotoMedia;
|
||||
class CloudImageView;
|
||||
@@ -51,7 +56,7 @@ namespace Toast {
|
||||
class Instance;
|
||||
} // namespace Toast
|
||||
namespace Platform {
|
||||
class TitleControls;
|
||||
struct SeparateTitleControls;
|
||||
} // namespace Platform
|
||||
} // namespace Ui
|
||||
|
||||
@@ -194,9 +199,11 @@ private:
|
||||
rpl::variable<PanelMode> _mode;
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
std::unique_ptr<Ui::Platform::TitleControls> _controls;
|
||||
std::unique_ptr<Ui::Platform::SeparateTitleControls> _controls;
|
||||
#endif // !Q_OS_MAC
|
||||
|
||||
const std::unique_ptr<base::PowerSaveBlocker> _powerSaveBlocker;
|
||||
|
||||
rpl::lifetime _callLifetime;
|
||||
|
||||
object_ptr<Ui::FlatLabel> _title = { nullptr };
|
||||
|
||||
@@ -129,7 +129,8 @@ object_ptr<ShareBox> ShareInviteLinkBox(
|
||||
auto submitCallback = [=](
|
||||
std::vector<not_null<PeerData*>> &&result,
|
||||
TextWithTags &&comment,
|
||||
Api::SendOptions options) {
|
||||
Api::SendOptions options,
|
||||
Data::ForwardOptions) {
|
||||
if (*sending || result.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||