Compare commits
318 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d1a4cdbfe | ||
|
|
383e6dec43 | ||
|
|
85904e3022 | ||
|
|
f88b97553e | ||
|
|
63c6a1db82 | ||
|
|
ca97e3c375 | ||
|
|
ef30c776bf | ||
|
|
d45e74619d | ||
|
|
d92b5eebcc | ||
|
|
5c6b4d95b0 | ||
|
|
0fbec5eba1 | ||
|
|
ab13d9bdaf | ||
|
|
0165e31ca7 | ||
|
|
f1e75d809a | ||
|
|
c776f81dc7 | ||
|
|
9fd62d3892 | ||
|
|
793906ca9a | ||
|
|
35e575c2d7 | ||
|
|
f5e84220eb | ||
|
|
1d622fb3c0 | ||
|
|
d8d3dda2f3 | ||
|
|
e098922a4b | ||
|
|
413ddf285e | ||
|
|
7ac78be984 | ||
|
|
4c546156da | ||
|
|
db528b39e1 | ||
|
|
586744c112 | ||
|
|
7b106761be | ||
|
|
8fb7f0fc73 | ||
|
|
10b169f9f6 | ||
|
|
c83b8d4043 | ||
|
|
1fc2b19c94 | ||
|
|
fb97940cac | ||
|
|
16c38b54e2 | ||
|
|
7f29f57c3d | ||
|
|
1fb1d57a27 | ||
|
|
47d7bd95ae | ||
|
|
368eeaf754 | ||
|
|
1686eb394d | ||
|
|
02586ebe4b | ||
|
|
8f80c19ae1 | ||
|
|
1598165e2b | ||
|
|
f4cd84c313 | ||
|
|
9b574e497d | ||
|
|
6660338ccc | ||
|
|
423ea5b499 | ||
|
|
4695ebae6e | ||
|
|
aaa4db7b27 | ||
|
|
0965b06fa3 | ||
|
|
be96bf2812 | ||
|
|
b7aa60bedf | ||
|
|
d5b3fa017b | ||
|
|
36fbdfb380 | ||
|
|
d0c78eaddd | ||
|
|
6513422e40 | ||
|
|
f066e0f05a | ||
|
|
249f7813c1 | ||
|
|
29a498b959 | ||
|
|
ae9ed820ee | ||
|
|
803593cd8d | ||
|
|
897e432f40 | ||
|
|
50e0c3ee4d | ||
|
|
056945d9f5 | ||
|
|
a9b70a7d63 | ||
|
|
6dabd87df3 | ||
|
|
b35b6c4449 | ||
|
|
74ef8104a7 | ||
|
|
af0eebb6f1 | ||
|
|
dbb46ce9b0 | ||
|
|
700d3db4cc | ||
|
|
64cf0e1a44 | ||
|
|
7ad660a0e7 | ||
|
|
e27d2bc2d5 | ||
|
|
24fed8105c | ||
|
|
9ce59730ff | ||
|
|
3f26fc9f55 | ||
|
|
0834920db8 | ||
|
|
f4ed2c26ba | ||
|
|
c63e2c01ac | ||
|
|
c61f3a0aba | ||
|
|
3c9ca2eb94 | ||
|
|
33c1c48ad9 | ||
|
|
a27aea3887 | ||
|
|
ea4044e38c | ||
|
|
c967a72dcb | ||
|
|
7d386b164b | ||
|
|
ccbbf6f5f3 | ||
|
|
9725d4272e | ||
|
|
eb75859dc0 | ||
|
|
ad5507f2c8 | ||
|
|
58f82620e0 | ||
|
|
053eace154 | ||
|
|
d64014c995 | ||
|
|
44ec55b6a8 | ||
|
|
9dba723643 | ||
|
|
97a82762ef | ||
|
|
1542311d89 | ||
|
|
fb322b5fc5 | ||
|
|
581a21dbd9 | ||
|
|
3d431a27cb | ||
|
|
cbb9657044 | ||
|
|
3797753d16 | ||
|
|
37aabc0da9 | ||
|
|
956c3af0ae | ||
|
|
1329870c8e | ||
|
|
ff6365ec72 | ||
|
|
1e9c79ca85 | ||
|
|
40f12a2584 | ||
|
|
97bab388ea | ||
|
|
bf616036b3 | ||
|
|
669b79588e | ||
|
|
33f4946242 | ||
|
|
888e42df34 | ||
|
|
70c79eb6bd | ||
|
|
bdd3c51ab8 | ||
|
|
6ca43153bb | ||
|
|
7db53599e8 | ||
|
|
61647275e8 | ||
|
|
a37138aa52 | ||
|
|
1504136828 | ||
|
|
c12356a032 | ||
|
|
126ed6e6e3 | ||
|
|
fa4236e9ea | ||
|
|
b19dcf0653 | ||
|
|
77d1f64e0e | ||
|
|
3479a4ec59 | ||
|
|
bdf28370f9 | ||
|
|
cd81fc6727 | ||
|
|
7351641034 | ||
|
|
e0669e222d | ||
|
|
4c1f83daca | ||
|
|
ced2652deb | ||
|
|
8d1db85a28 | ||
|
|
97305c8cb5 | ||
|
|
1ef5d81270 | ||
|
|
9ff427afad | ||
|
|
1c5eadcd79 | ||
|
|
bc6c01de7f | ||
|
|
41255cab44 | ||
|
|
ccbc63cd6e | ||
|
|
97446ae783 | ||
|
|
5a75dd2b6f | ||
|
|
6559e83e83 | ||
|
|
d679703bbf | ||
|
|
66a3e36024 | ||
|
|
31e38e1690 | ||
|
|
da10059f45 | ||
|
|
cb5863177f | ||
|
|
84399286c1 | ||
|
|
2e92441b3a | ||
|
|
7883f97c94 | ||
|
|
297b5d6a76 | ||
|
|
492dc2568c | ||
|
|
547c657b1a | ||
|
|
c478d96385 | ||
|
|
2ede53e0ee | ||
|
|
6f760d513e | ||
|
|
f4f6550d66 | ||
|
|
c7878f9d21 | ||
|
|
cd75a45673 | ||
|
|
07e3671ca8 | ||
|
|
295aa644bf | ||
|
|
b5b78c0ade | ||
|
|
f5c0e5d31d | ||
|
|
246ed43046 | ||
|
|
701e1d7b4d | ||
|
|
9cbe899688 | ||
|
|
7409d615a3 | ||
|
|
c9553c2d4c | ||
|
|
bedefaee4d | ||
|
|
5d3b8f02fc | ||
|
|
5120d3ef2c | ||
|
|
82a372873f | ||
|
|
d1d1f83881 | ||
|
|
78c3c86fe6 | ||
|
|
447d4e6c47 | ||
|
|
d0e3d15e8e | ||
|
|
0251f58bf2 | ||
|
|
36997f084a | ||
|
|
942fcb9aae | ||
|
|
6232dce1a3 | ||
|
|
0c0fc46b90 | ||
|
|
dcf737bebe | ||
|
|
919834093e | ||
|
|
99ccd49e13 | ||
|
|
29896b2efd | ||
|
|
1b7f3db43a | ||
|
|
1fa22398a9 | ||
|
|
0e16b3fe69 | ||
|
|
462020d54c | ||
|
|
9c17147f60 | ||
|
|
0bf933b009 | ||
|
|
27f6c8ce62 | ||
|
|
3135463017 | ||
|
|
89950de93e | ||
|
|
13c2d6ff72 | ||
|
|
5e70bf64c6 | ||
|
|
3260e9e752 | ||
|
|
1f16ac59ca | ||
|
|
87bf0654a2 | ||
|
|
6adcf660f1 | ||
|
|
038d8f1781 | ||
|
|
a5977f5f7a | ||
|
|
2143864fd5 | ||
|
|
73691e795b | ||
|
|
c0246a9373 | ||
|
|
1af394a485 | ||
|
|
5180d31b40 | ||
|
|
07c8aae225 | ||
|
|
3c1c17ef80 | ||
|
|
b79ecb5909 | ||
|
|
b98f0933af | ||
|
|
05dcd6fc9c | ||
|
|
19bcc145ad | ||
|
|
4ae760dd7e | ||
|
|
cad4d19272 | ||
|
|
ae64747489 | ||
|
|
db5d599052 | ||
|
|
cc463b07b1 | ||
|
|
d8e55081b0 | ||
|
|
9c66bd553a | ||
|
|
a6bb180e22 | ||
|
|
38ca3ba341 | ||
|
|
8ef00dc4ff | ||
|
|
1630ad0804 | ||
|
|
c3c482aa50 | ||
|
|
f4a63e1e9d | ||
|
|
161e51757c | ||
|
|
46d4b03d49 | ||
|
|
48743a7973 | ||
|
|
6709147560 | ||
|
|
0b2d4326e7 | ||
|
|
ca49e74b6f | ||
|
|
95b4f56b86 | ||
|
|
9828262a03 | ||
|
|
f76e094e98 | ||
|
|
067e52f5d1 | ||
|
|
4efd649c27 | ||
|
|
ff25f1d5c9 | ||
|
|
dd78052f92 | ||
|
|
8a4c7e3994 | ||
|
|
44e71dfa03 | ||
|
|
b6e184d0c8 | ||
|
|
042ed8f54a | ||
|
|
aabc8173c3 | ||
|
|
c14e20b33f | ||
|
|
266c1531ce | ||
|
|
8d632bd2be | ||
|
|
c70a1f03de | ||
|
|
17de6c1ff3 | ||
|
|
4e210e40a2 | ||
|
|
e13593b095 | ||
|
|
e149f10d40 | ||
|
|
7f890122e6 | ||
|
|
422831fa79 | ||
|
|
7494468f1f | ||
|
|
7bc86cc9af | ||
|
|
c1f3fe1961 | ||
|
|
cfd733c54c | ||
|
|
3fa5e004fe | ||
|
|
862e4e45ad | ||
|
|
53df4d1b10 | ||
|
|
5cfd402b70 | ||
|
|
57e9651a8a | ||
|
|
53d206c12c | ||
|
|
46f3cf3395 | ||
|
|
dfc0491524 | ||
|
|
54f757e770 | ||
|
|
abfd3ad1b9 | ||
|
|
bed208d621 | ||
|
|
33c453a13c | ||
|
|
e118972d5c | ||
|
|
fb8a9a930c | ||
|
|
7a9cfcc40d | ||
|
|
e1dc15321a | ||
|
|
2b5e575b67 | ||
|
|
42e216603c | ||
|
|
d46e145c61 | ||
|
|
5dcb232b77 | ||
|
|
b34d5b8306 | ||
|
|
76d81ff197 | ||
|
|
71637d2a0e | ||
|
|
423daecbde | ||
|
|
3cb76fb80b | ||
|
|
6882093ed1 | ||
|
|
699761b42f | ||
|
|
f50c50a152 | ||
|
|
13d22947df | ||
|
|
6c08bab550 | ||
|
|
3e2f4bed50 | ||
|
|
41d39012d2 | ||
|
|
a7764f84f0 | ||
|
|
85fcec2fb5 | ||
|
|
82e835fbc2 | ||
|
|
80684d9073 | ||
|
|
b04f0e0d3d | ||
|
|
65cc9bcd87 | ||
|
|
bc06a3aea3 | ||
|
|
de78f4255e | ||
|
|
d67dafaccb | ||
|
|
4f8ea4c807 | ||
|
|
15b19f8565 | ||
|
|
b16696db93 | ||
|
|
63129072ba | ||
|
|
1fdd591aa0 | ||
|
|
f370ca97d0 | ||
|
|
f5aba5a907 | ||
|
|
1d613995db | ||
|
|
5bb1c77199 | ||
|
|
5b39c7013a | ||
|
|
ed91c07f99 | ||
|
|
a66b2a4056 | ||
|
|
a1a7399023 | ||
|
|
e71b7dd384 | ||
|
|
664b43acd7 | ||
|
|
eac867ce85 | ||
|
|
2ad48f18f2 | ||
|
|
e823fe5891 |
2
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
@@ -25,6 +25,8 @@ Tell us what happens instead
|
||||
|
||||
**Version of Telegram Desktop:**
|
||||
|
||||
**Installation source (Linux Only)** - the official website / GitHub releases / flatpak / snap / distribution package:
|
||||
|
||||
**Used theme**:
|
||||
|
||||
<details><summary><b>Logs</b>:</summary>
|
||||
|
||||
46
.github/workflows/issue_closer.yml
vendored
@@ -14,6 +14,22 @@ jobs:
|
||||
echo $tag
|
||||
echo ::set-env name=LATEST_TAG::$tag
|
||||
|
||||
- name: Get the latest macOS version.
|
||||
shell: python
|
||||
run: |
|
||||
import subprocess;
|
||||
from xml.dom import minidom;
|
||||
|
||||
url = "https://osx.telegram.org/updates/versions.xml";
|
||||
subprocess.check_call("wget %s" % url, shell=True);
|
||||
|
||||
xmldoc = minidom.parse('versions.xml');
|
||||
itemlist = xmldoc.getElementsByTagName('enclosure');
|
||||
ver = itemlist[0].attributes['sparkle:shortVersionString'].value;
|
||||
print(ver);
|
||||
|
||||
subprocess.check_call("echo ::set-env name=%s::%s" % ("LATEST_MACOS", ver), shell=True);
|
||||
|
||||
- name: Check a version from an issue.
|
||||
uses: actions/github-script@0.4.0
|
||||
with:
|
||||
@@ -21,14 +37,24 @@ jobs:
|
||||
script: |
|
||||
let errorStr = "Version not found.";
|
||||
|
||||
function maxIndexOf(str, i) {
|
||||
let index = str.indexOf(i);
|
||||
return (index == -1) ? Number.MAX_SAFE_INTEGER : index;
|
||||
}
|
||||
|
||||
let item1 = "Version of Telegram Desktop";
|
||||
let item2 = "Used theme";
|
||||
let item2 = "Installation source";
|
||||
let item3 = "Used theme";
|
||||
let item4 = "<details>";
|
||||
let body = context.payload.issue.body;
|
||||
|
||||
console.log("Body of issue:\n" + body);
|
||||
let index1 = body.indexOf(item1);
|
||||
let index2 = body.indexOf(item2);
|
||||
index2 = (index2 == -1) ? Number.MAX_SAFE_INTEGER : index2;
|
||||
let index2 = Math.min(
|
||||
Math.min(
|
||||
maxIndexOf(body, item2),
|
||||
maxIndexOf(body, item3)),
|
||||
maxIndexOf(body, item4));
|
||||
|
||||
console.log("Index 1: " + index1);
|
||||
console.log("Index 2: " + index2);
|
||||
@@ -65,10 +91,20 @@ jobs:
|
||||
let issueNum = firstNum(issueVer);
|
||||
let latestNum = firstNum(latestVer);
|
||||
|
||||
if (issueNum <= latestNum && issueNum < 5) {
|
||||
let macos_ver = process.env.LATEST_MACOS;
|
||||
console.log("Telegram for MacOS version from website: " + macos_ver);
|
||||
|
||||
if (issueNum <= latestNum && issueNum < macos_ver) {
|
||||
console.log("Seems the version of this issue is fine!");
|
||||
return;
|
||||
}
|
||||
if (issueNum > macos_ver) {
|
||||
let message = `Seems like it's neither the Telegram Desktop\
|
||||
nor the Telegram for macOS version.
|
||||
`;
|
||||
console.log(message);
|
||||
return;
|
||||
}
|
||||
|
||||
let message = `
|
||||
Sorry, but according to the version you specify in this issue, \
|
||||
@@ -77,7 +113,7 @@ jobs:
|
||||
You can report your issue to [the group](https://t.me/macswift) \
|
||||
or to [the repository of Telegram for macOS](https://github.com/overtake/TelegramSwift).
|
||||
|
||||
If I made a mistake and closed your issue wrongly, please reopen it. Thanks!
|
||||
**If I made a mistake and closed your issue wrongly, please reopen it. Thanks!**
|
||||
`;
|
||||
|
||||
let params = {
|
||||
|
||||
177
.github/workflows/linux.yml
vendored
@@ -4,11 +4,43 @@ on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
- '**.md'
|
||||
- '!docs/building-cmake.md'
|
||||
- 'changelog.txt'
|
||||
- 'LEGAL'
|
||||
- 'LICENSE'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/linux.yml'
|
||||
- 'snap/**'
|
||||
- 'Telegram/build/**'
|
||||
- 'Telegram/Patches/**'
|
||||
- 'Telegram/Resources/uwp/**'
|
||||
- 'Telegram/Resources/winrc/**'
|
||||
- 'Telegram/SourceFiles/platform/win/**'
|
||||
- 'Telegram/SourceFiles/platform/mac/**'
|
||||
- 'Telegram/Telegram/**'
|
||||
- 'Telegram/configure.bat'
|
||||
- 'Telegram/Telegram.plist'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
- '**.md'
|
||||
- '!docs/building-cmake.md'
|
||||
- 'changelog.txt'
|
||||
- 'LEGAL'
|
||||
- 'LICENSE'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/linux.yml'
|
||||
- 'snap/**'
|
||||
- 'Telegram/build/**'
|
||||
- 'Telegram/Patches/**'
|
||||
- 'Telegram/Resources/uwp/**'
|
||||
- 'Telegram/Resources/winrc/**'
|
||||
- 'Telegram/SourceFiles/platform/win/**'
|
||||
- 'Telegram/SourceFiles/platform/mac/**'
|
||||
- 'Telegram/Telegram/**'
|
||||
- 'Telegram/configure.bat'
|
||||
- 'Telegram/Telegram.plist'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -25,8 +57,8 @@ jobs:
|
||||
|
||||
env:
|
||||
GIT: "https://github.com"
|
||||
QT: "5_12_5"
|
||||
QT_PREFIX: "/usr/local/desktop-app/Qt-5.12.5"
|
||||
QT: "5_12_8"
|
||||
QT_PREFIX: "/usr/local/desktop-app/Qt-5.12.8"
|
||||
OPENSSL_VER: "1_1_1"
|
||||
OPENSSL_PREFIX: "/usr/local/desktop-app/openssl-1.1.1"
|
||||
CMAKE_VER: "3.17.0"
|
||||
@@ -101,14 +133,6 @@ jobs:
|
||||
cd Libraries
|
||||
echo ::set-env name=LibrariesPath::`pwd`
|
||||
|
||||
- name: Range-v3.
|
||||
run: |
|
||||
echo "Find necessary branch from doc."
|
||||
cloneRange=$(grep -A 1 "range-v3" $REPO_NAME/$DOC_PATH | sed -n 1p)
|
||||
cd $LibrariesPath
|
||||
echo $cloneRange
|
||||
eval $cloneRange
|
||||
|
||||
- name: Patches.
|
||||
run: |
|
||||
echo "Find necessary commit from doc."
|
||||
@@ -189,23 +213,29 @@ jobs:
|
||||
|
||||
git clone --branch release/3.4 $GIT/FFmpeg/FFmpeg ffmpeg
|
||||
cd ffmpeg
|
||||
./configure --prefix=$LibrariesPath/ffmpeg-cache \
|
||||
--enable-protocol=file --enable-libopus \
|
||||
./configure \
|
||||
--disable-debug \
|
||||
--disable-programs \
|
||||
--disable-doc \
|
||||
--disable-network \
|
||||
--disable-autodetect \
|
||||
--disable-everything \
|
||||
--disable-neon \
|
||||
--disable-alsa \
|
||||
--disable-iconv \
|
||||
--enable-libopus \
|
||||
--enable-vaapi \
|
||||
--enable-vdpau \
|
||||
--enable-protocol=file \
|
||||
--enable-hwaccel=h264_vaapi \
|
||||
--enable-hwaccel=h264_vdpau \
|
||||
--enable-hwaccel=mpeg4_vaapi \
|
||||
--enable-hwaccel=mpeg4_vdpau \
|
||||
--enable-decoder=aac \
|
||||
--enable-decoder=aac_at \
|
||||
--enable-decoder=aac_fixed \
|
||||
--enable-decoder=aac_latm \
|
||||
--enable-decoder=aasc \
|
||||
--enable-decoder=alac \
|
||||
--enable-decoder=alac_at \
|
||||
--enable-decoder=flac \
|
||||
--enable-decoder=gif \
|
||||
--enable-decoder=h264 \
|
||||
@@ -227,14 +257,12 @@ jobs:
|
||||
--enable-decoder=msmpeg4v3 \
|
||||
--enable-decoder=opus \
|
||||
--enable-decoder=pcm_alaw \
|
||||
--enable-decoder=pcm_alaw_at \
|
||||
--enable-decoder=pcm_f32be \
|
||||
--enable-decoder=pcm_f32le \
|
||||
--enable-decoder=pcm_f64be \
|
||||
--enable-decoder=pcm_f64le \
|
||||
--enable-decoder=pcm_lxf \
|
||||
--enable-decoder=pcm_mulaw \
|
||||
--enable-decoder=pcm_mulaw_at \
|
||||
--enable-decoder=pcm_s16be \
|
||||
--enable-decoder=pcm_s16be_planar \
|
||||
--enable-decoder=pcm_s16le \
|
||||
@@ -289,7 +317,7 @@ jobs:
|
||||
--enable-muxer=opus
|
||||
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
sudo make DESTDIR="$LibrariesPath/ffmpeg-cache" install
|
||||
cd ..
|
||||
rm -rf ffmpeg
|
||||
- name: FFmpeg install.
|
||||
@@ -298,7 +326,7 @@ jobs:
|
||||
#List of files from cmake/external/ffmpeg/CMakeLists.txt.
|
||||
copyLib() {
|
||||
mkdir -p ffmpeg/$1
|
||||
yes | cp -i ffmpeg-cache/lib/$1.a ffmpeg/$1/$1.a
|
||||
yes | cp -i ffmpeg-cache/usr/local/lib/$1.a ffmpeg/$1/$1.a
|
||||
}
|
||||
copyLib libavformat
|
||||
copyLib libavcodec
|
||||
@@ -306,20 +334,7 @@ jobs:
|
||||
copyLib libswscale
|
||||
copyLib libavutil
|
||||
|
||||
sudo cp -R ffmpeg-cache/. /usr/local/
|
||||
|
||||
- name: PortAudio.
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
|
||||
git clone https://git.assembla.com/portaudio.git
|
||||
cd portaudio
|
||||
git checkout 396fe4b669
|
||||
./configure
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
cd ..
|
||||
rm -rf portaudio
|
||||
sudo cp -R ffmpeg-cache/. /
|
||||
|
||||
- name: OpenAL Soft.
|
||||
run: |
|
||||
@@ -327,7 +342,13 @@ jobs:
|
||||
|
||||
git clone -b openal-soft-1.20.1 --depth=1 $GIT/kcat/openal-soft.git
|
||||
cd openal-soft/build
|
||||
cmake -D LIBTYPE:STRING=STATIC ..
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLIBTYPE:STRING=STATIC \
|
||||
-DALSOFT_EXAMPLES=OFF \
|
||||
-DALSOFT_TESTS=OFF \
|
||||
-DALSOFT_UTILS=OFF \
|
||||
-DALSOFT_CONFIG=OFF
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
cd -
|
||||
@@ -348,28 +369,15 @@ jobs:
|
||||
git clone -b OpenSSL_${OPENSSL_VER}-stable --depth=1 \
|
||||
$GIT/openssl/openssl $opensslDir
|
||||
cd $opensslDir
|
||||
./config --prefix=$LibrariesPath/openssl-cache
|
||||
./config --prefix="$OPENSSL_PREFIX"
|
||||
make -j$(nproc)
|
||||
sudo make install_sw
|
||||
sudo make DESTDIR="$LibrariesPath/openssl-cache" install_sw
|
||||
cd ..
|
||||
rm -rf $opensslDir
|
||||
- name: OpenSSL install.
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
sudo mkdir -p $OPENSSL_PREFIX
|
||||
sudo cp -R openssl-cache/. $OPENSSL_PREFIX/
|
||||
|
||||
- name: Libxkbcommon.
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
|
||||
git clone -b xkbcommon-0.8.4 --depth=1 $GIT/xkbcommon/libxkbcommon.git
|
||||
cd libxkbcommon
|
||||
./autogen.sh
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
cd ..
|
||||
rm -rf libxkbcommon
|
||||
sudo cp -R openssl-cache/. /
|
||||
|
||||
- name: Libwayland.
|
||||
run: |
|
||||
@@ -377,38 +385,55 @@ jobs:
|
||||
|
||||
git clone -b 1.16 https://gitlab.freedesktop.org/wayland/wayland
|
||||
cd wayland
|
||||
./autogen.sh --enable-static --disable-documentation
|
||||
./autogen.sh \
|
||||
--enable-static \
|
||||
--disable-documentation \
|
||||
--disable-dtd-validation
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
cd ..
|
||||
rm -rf wayland
|
||||
|
||||
- name: Qt 5.12.5 cache.
|
||||
- name: Libxkbcommon.
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
|
||||
git clone -b xkbcommon-0.8.4 --depth=1 $GIT/xkbcommon/libxkbcommon.git
|
||||
cd libxkbcommon
|
||||
./autogen.sh \
|
||||
--disable-docs \
|
||||
--disable-wayland \
|
||||
--with-xkb-config-root=/usr/share/X11/xkb \
|
||||
--with-x-locale-root=/usr/share/X11/locale
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
cd ..
|
||||
rm -rf libxkbcommon
|
||||
|
||||
- name: Qt 5.12.8 cache.
|
||||
id: cache-qt
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/qt-cache
|
||||
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_5_12_5.diff') }}
|
||||
- name: Qt 5.12.5 build.
|
||||
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qt*_5_12_8/*') }}
|
||||
- name: Qt 5.12.8 build.
|
||||
if: steps.cache-qt.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
|
||||
git clone -b v5.12.5 --depth=1 git://code.qt.io/qt/qt5.git qt_${QT}
|
||||
git clone -b v5.12.8 --depth=1 git://code.qt.io/qt/qt5.git qt_${QT}
|
||||
cd qt_${QT}
|
||||
perl init-repository --module-subset=qtbase,qtwayland,qtimageformats,qtsvg
|
||||
git submodule update qtbase qtwayland qtimageformats qtsvg
|
||||
perl init-repository --module-subset=qtbase,qtwayland,qtimageformats,qtsvg,qtx11extras
|
||||
git submodule update qtbase qtwayland qtimageformats qtsvg qtx11extras
|
||||
cd qtbase
|
||||
git apply ../../patches/qtbase_${QT}.diff
|
||||
cd src/plugins/platforminputcontexts
|
||||
git clone $GIT/desktop-app/fcitx.git
|
||||
git clone $GIT/desktop-app/hime.git
|
||||
git clone $GIT/desktop-app/nimf.git
|
||||
cd ../../../..
|
||||
find ../../patches/qtbase_${QT} -type f -print0 | sort -z | xargs -r0 git apply
|
||||
cd ..
|
||||
cd qtwayland
|
||||
find ../../patches/qtwayland_${QT} -type f -print0 | sort -z | xargs -r0 git apply
|
||||
cd ..
|
||||
|
||||
./configure -prefix "$LibrariesPath/qt-cache" \
|
||||
./configure -prefix "$QT_PREFIX" \
|
||||
-release \
|
||||
-force-debug-info \
|
||||
-opensource \
|
||||
-confirm-license \
|
||||
-qt-zlib \
|
||||
@@ -417,8 +442,6 @@ jobs:
|
||||
-qt-harfbuzz \
|
||||
-qt-pcre \
|
||||
-qt-xcb \
|
||||
-system-freetype \
|
||||
-fontconfig \
|
||||
-no-gtk \
|
||||
-static \
|
||||
-dbus-runtime \
|
||||
@@ -428,14 +451,13 @@ jobs:
|
||||
-nomake tests
|
||||
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
sudo make INSTALL_ROOT="$LibrariesPath/qt-cache" install
|
||||
cd ..
|
||||
rm -rf qt_${QT}
|
||||
- name: Qt 5.12.5 install.
|
||||
- name: Qt 5.12.8 install.
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
sudo mkdir -p $QT_PREFIX
|
||||
sudo cp -R qt-cache/. $QT_PREFIX/
|
||||
sudo cp -R qt-cache/. /
|
||||
|
||||
- name: Breakpad cache.
|
||||
id: cache-breakpad
|
||||
@@ -467,9 +489,9 @@ jobs:
|
||||
cd ..
|
||||
|
||||
cd breakpad
|
||||
./configure --prefix=$BreakpadCache
|
||||
./configure
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
sudo make DESTDIR="$BreakpadCache" install
|
||||
cd src
|
||||
rm -r testing
|
||||
git clone $GIT/google/googletest testing
|
||||
@@ -486,7 +508,7 @@ jobs:
|
||||
- name: Breakpad install.
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
sudo cp -R breakpad-cache/. /usr/local/
|
||||
sudo cp -R breakpad-cache/. /
|
||||
mkdir -p breakpad/out/Default/
|
||||
cp breakpad-cache/dump_syms breakpad/out/Default/dump_syms
|
||||
|
||||
@@ -499,6 +521,9 @@ jobs:
|
||||
if [ -n "${{ matrix.defines }}" ]; then
|
||||
DEFINE="-D ${{ matrix.defines }}=ON"
|
||||
echo Define from matrix: $DEFINE
|
||||
echo ::set-env name=ARTIFACT_NAME::Telegram_${{ matrix.defines }}
|
||||
else
|
||||
echo ::set-env name=ARTIFACT_NAME::Telegram
|
||||
fi
|
||||
|
||||
./configure.sh \
|
||||
@@ -534,5 +559,5 @@ jobs:
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
name: Upload artifact.
|
||||
with:
|
||||
name: Telegram
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: ${{ env.REPO_NAME }}/out/Debug/bin/artifact/
|
||||
|
||||
68
.github/workflows/mac.yml
vendored
@@ -4,11 +4,41 @@ on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
- '**.md'
|
||||
- '!docs/building-xcode.md'
|
||||
- 'changelog.txt'
|
||||
- 'LEGAL'
|
||||
- 'LICENSE'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/mac.yml'
|
||||
- 'lib/xdg/**'
|
||||
- 'snap/**'
|
||||
- 'Telegram/build/**'
|
||||
- 'Telegram/Patches/**'
|
||||
- 'Telegram/Resources/uwp/**'
|
||||
- 'Telegram/Resources/winrc/**'
|
||||
- 'Telegram/SourceFiles/platform/win/**'
|
||||
- 'Telegram/SourceFiles/platform/linux/**'
|
||||
- 'Telegram/configure.bat'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
- '**.md'
|
||||
- '!docs/building-xcode.md'
|
||||
- 'changelog.txt'
|
||||
- 'LEGAL'
|
||||
- 'LICENSE'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/mac.yml'
|
||||
- 'lib/xdg/**'
|
||||
- 'snap/**'
|
||||
- 'Telegram/build/**'
|
||||
- 'Telegram/Patches/**'
|
||||
- 'Telegram/Resources/uwp/**'
|
||||
- 'Telegram/Resources/winrc/**'
|
||||
- 'Telegram/SourceFiles/platform/win/**'
|
||||
- 'Telegram/SourceFiles/platform/linux/**'
|
||||
- 'Telegram/configure.bat'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -27,9 +57,9 @@ jobs:
|
||||
PREFIX: "/usr/local/macos"
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.12"
|
||||
XZ: "xz-5.2.4"
|
||||
QT: "5_12_5"
|
||||
QT: "5_12_8"
|
||||
OPENSSL_VER: "1_1_1"
|
||||
QT_PREFIX: "/usr/local/desktop-app/Qt-5.12.5"
|
||||
QT_PREFIX: "/usr/local/desktop-app/Qt-5.12.8"
|
||||
LIBICONV_VER: "libiconv-1.16"
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
ONLY_CACHE: "false"
|
||||
@@ -58,6 +88,7 @@ jobs:
|
||||
echo $MIN_MAC >> CACHE_KEY.txt
|
||||
echo $PREFIX >> CACHE_KEY.txt
|
||||
echo $MANUAL_CACHING >> CACHE_KEY.txt
|
||||
echo "$GITHUB_WORKSPACE" >> CACHE_KEY.txt
|
||||
if [ "$AUTO_CACHING" == "1" ]; then
|
||||
thisFile=$REPO_NAME/.github/workflows/mac.yml
|
||||
echo `md5 -q $thisFile` >> CACHE_KEY.txt
|
||||
@@ -70,14 +101,6 @@ jobs:
|
||||
cd Libraries/macos
|
||||
echo ::set-env name=LibrariesPath::`pwd`
|
||||
|
||||
- name: Range-v3.
|
||||
run: |
|
||||
echo "Find necessary branch from doc."
|
||||
cloneRange=$(grep -A 1 "range-v3" $REPO_NAME/$DOC_PATH | sed -n 1p)
|
||||
cd $LibrariesPath
|
||||
echo $cloneRange
|
||||
eval $cloneRange
|
||||
|
||||
- name: Patches.
|
||||
run: |
|
||||
echo "Find necessary commit from doc."
|
||||
@@ -376,20 +399,20 @@ jobs:
|
||||
build/gyp_crashpad.py -Dmac_deployment_target=10.10
|
||||
ninja -C out/Debug
|
||||
|
||||
- name: Qt 5.12.5 cache.
|
||||
- name: Qt 5.12.8 cache.
|
||||
id: cache-qt
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/qt-cache
|
||||
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_5_12_5.diff') }}
|
||||
- name: Use cached Qt 5.12.5.
|
||||
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_5_12_8.diff') }}
|
||||
- name: Use cached Qt 5.12.8.
|
||||
if: steps.cache-qt.outputs.cache-hit == 'true'
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
mv qt-cache Qt-5.12.5
|
||||
mv qt-cache Qt-5.12.8
|
||||
sudo mkdir -p $QT_PREFIX
|
||||
sudo mv -f Qt-5.12.5 "$(dirname "$QT_PREFIX")"/
|
||||
- name: Qt 5.12.5 build.
|
||||
sudo mv -f Qt-5.12.8 "$(dirname "$QT_PREFIX")"/
|
||||
- name: Qt 5.12.8 build.
|
||||
if: steps.cache-qt.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
@@ -397,11 +420,11 @@ jobs:
|
||||
git clone git://code.qt.io/qt/qt5.git qt$QT
|
||||
cd qt$QT
|
||||
perl init-repository --module-subset=qtbase,qtimageformats
|
||||
git checkout v5.12.5
|
||||
git checkout v5.12.8
|
||||
git submodule update qtbase
|
||||
git submodule update qtimageformats
|
||||
cd qtbase
|
||||
git apply ../../patches/qtbase_$QT.diff
|
||||
find ../../patches/qtbase_$QT -type f -print0 | sort -z | xargs -0 git apply
|
||||
cd ..
|
||||
|
||||
./configure \
|
||||
@@ -433,6 +456,9 @@ jobs:
|
||||
if [ -n "${{ matrix.defines }}" ]; then
|
||||
DEFINE="-D ${{ matrix.defines }}=ON"
|
||||
echo Define from matrix: $DEFINE
|
||||
echo ::set-env name=ARTIFACT_NAME::Telegram_${{ matrix.defines }}
|
||||
else
|
||||
echo ::set-env name=ARTIFACT_NAME::Telegram
|
||||
fi
|
||||
|
||||
./configure.sh -D TDESKTOP_API_TEST=ON -D DESKTOP_APP_USE_PACKAGED=OFF $DEFINE
|
||||
@@ -453,5 +479,5 @@ jobs:
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
name: Upload artifact.
|
||||
with:
|
||||
name: Telegram
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: ${{ env.REPO_NAME }}/out/Debug/artifact/
|
||||
|
||||
35
.github/workflows/master_updater.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: Master branch updater.
|
||||
|
||||
on:
|
||||
release:
|
||||
types: released
|
||||
|
||||
jobs:
|
||||
updater:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SKIP: "0"
|
||||
to_branch: "master"
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
if: env.SKIP == '0'
|
||||
- name: Push the code to the master branch.
|
||||
if: env.SKIP == '0'
|
||||
run: |
|
||||
token=${{ secrets.TOKEN_FOR_MASTER_UPDATER }}
|
||||
if [ -z "${token}" ]; then
|
||||
echo "Token is unset. Nothing to do."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
url=https://x-access-token:$token@github.com/$GITHUB_REPOSITORY
|
||||
latest_tag=$(git describe --tags --abbrev=0)
|
||||
echo "Latest tag: $latest_tag"
|
||||
|
||||
git remote set-url origin $url
|
||||
git remote -v
|
||||
git checkout master
|
||||
git merge $latest_tag
|
||||
|
||||
git push origin HEAD:refs/heads/$to_branch
|
||||
echo "Done!"
|
||||
89
.github/workflows/snap.yml
vendored
@@ -4,11 +4,41 @@ on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
- '**.md'
|
||||
- 'changelog.txt'
|
||||
- 'LEGAL'
|
||||
- 'LICENSE'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/snap.yml'
|
||||
- 'Telegram/build/**'
|
||||
- 'Telegram/Patches/**'
|
||||
- '!Telegram/Patches/ffmpeg.diff'
|
||||
- 'Telegram/Resources/uwp/**'
|
||||
- 'Telegram/Resources/winrc/**'
|
||||
- 'Telegram/SourceFiles/platform/win/**'
|
||||
- 'Telegram/SourceFiles/platform/mac/**'
|
||||
- 'Telegram/Telegram/**'
|
||||
- 'Telegram/configure.bat'
|
||||
- 'Telegram/Telegram.plist'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
- '**.md'
|
||||
- 'changelog.txt'
|
||||
- 'LEGAL'
|
||||
- 'LICENSE'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/snap.yml'
|
||||
- 'Telegram/build/**'
|
||||
- 'Telegram/Patches/**'
|
||||
- '!Telegram/Patches/ffmpeg.diff'
|
||||
- 'Telegram/Resources/uwp/**'
|
||||
- 'Telegram/Resources/winrc/**'
|
||||
- 'Telegram/SourceFiles/platform/win/**'
|
||||
- 'Telegram/SourceFiles/platform/mac/**'
|
||||
- 'Telegram/Telegram/**'
|
||||
- 'Telegram/configure.bat'
|
||||
- 'Telegram/Telegram.plist'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -18,8 +48,6 @@ jobs:
|
||||
|
||||
env:
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
ONLY_CACHE: "false"
|
||||
MANUAL_CACHING: "3"
|
||||
|
||||
steps:
|
||||
- name: Clone.
|
||||
@@ -29,60 +57,18 @@ jobs:
|
||||
|
||||
- name: First set up.
|
||||
run: |
|
||||
# Workaround for Heroku
|
||||
curl https://cli-assets.heroku.com/apt/release.key | sudo apt-key add -
|
||||
# Workaround for permanent problems with third-party repository keys
|
||||
sudo rm -rf /etc/apt/sources.list.d/*
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-8 g++-8 -y
|
||||
sudo snap install --classic snapcraft
|
||||
|
||||
# Workaround for snapcraft
|
||||
# See https://forum.snapcraft.io/t/13258
|
||||
sudo chown root:root /
|
||||
|
||||
md5() {
|
||||
md5cache=$(md5sum $1.txt | cut -c -32)
|
||||
echo ::set-env name=$1::$md5cache
|
||||
}
|
||||
keyFor() {
|
||||
keyName="${1^^}_CACHE_KEY"
|
||||
awk -v RS="" -v ORS="\n\n" '/^ '"$1"':/' snap/snapcraft.yaml > $keyName.txt
|
||||
md5 $keyName
|
||||
}
|
||||
|
||||
snapcraft --version > CACHE_KEY.txt
|
||||
gcc-8 --version >> CACHE_KEY.txt
|
||||
echo $MANUAL_CACHING >> CACHE_KEY.txt
|
||||
md5 CACHE_KEY
|
||||
|
||||
keyFor cmake
|
||||
keyFor ffmpeg
|
||||
|
||||
- name: CMake cache.
|
||||
id: cache-cmake
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: parts/cmake
|
||||
key: ${{ runner.OS }}-cmake-${{ env.CACHE_KEY }}-${{ env.CMAKE_CACHE_KEY }}
|
||||
|
||||
- name: CMake build.
|
||||
if: steps.cache-cmake.outputs.cache-hit != 'true'
|
||||
run: sudo snapcraft build --destructive-mode cmake
|
||||
|
||||
- name: FFmpeg cache.
|
||||
id: cache-ffmpeg
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: parts/ffmpeg
|
||||
key: ${{ runner.OS }}-ffmpeg-${{ env.CACHE_KEY }}-${{ env.FFMPEG_CACHE_KEY }}
|
||||
|
||||
- name: FFmpeg build.
|
||||
if: steps.cache-ffmpeg.outputs.cache-hit != 'true'
|
||||
run: sudo snapcraft build --destructive-mode ffmpeg
|
||||
|
||||
- name: Telegram Desktop snap build.
|
||||
if: env.ONLY_CACHE == 'false'
|
||||
run: sudo snapcraft --destructive-mode
|
||||
run: sudo snap run snapcraft --destructive-mode
|
||||
|
||||
- name: Move artifact.
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
@@ -99,8 +85,3 @@ jobs:
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: artifact
|
||||
|
||||
- name: Remove unneeded directories for cache.
|
||||
run: |
|
||||
sudo rm -rf parts/{cmake,ffmpeg}/{build,src,ubuntu}
|
||||
sudo rm -rf parts/{cmake,ffmpeg}/state/{stage,prime}
|
||||
|
||||
71
.github/workflows/win.yml
vendored
@@ -4,11 +4,46 @@ on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
- '**.md'
|
||||
- '!docs/building-msvc.md'
|
||||
- 'changelog.txt'
|
||||
- 'LEGAL'
|
||||
- 'LICENSE'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/win.yml'
|
||||
- 'lib/xdg/**'
|
||||
- 'snap/**'
|
||||
- 'Telegram/build/**'
|
||||
- 'Telegram/Patches/**'
|
||||
- '!Telegram/Patches/build_ffmpeg_win.sh'
|
||||
- 'Telegram/Resources/uwp/**'
|
||||
- 'Telegram/SourceFiles/platform/linux/**'
|
||||
- 'Telegram/SourceFiles/platform/mac/**'
|
||||
- 'Telegram/Telegram/**'
|
||||
- 'Telegram/configure.sh'
|
||||
- 'Telegram/Telegram.plist'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
- '**.md'
|
||||
- '!docs/building-msvc.md'
|
||||
- 'changelog.txt'
|
||||
- 'LEGAL'
|
||||
- 'LICENSE'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/win.yml'
|
||||
- 'lib/xdg/**'
|
||||
- 'snap/**'
|
||||
- 'Telegram/build/**'
|
||||
- 'Telegram/Patches/**'
|
||||
- '!Telegram/Patches/build_ffmpeg_win.sh'
|
||||
- 'Telegram/Resources/uwp/**'
|
||||
- 'Telegram/SourceFiles/platform/linux/**'
|
||||
- 'Telegram/SourceFiles/platform/mac/**'
|
||||
- '!Telegram/Patches/breakpad.diff'
|
||||
- 'Telegram/Telegram/**'
|
||||
- 'Telegram/configure.sh'
|
||||
- 'Telegram/Telegram.plist'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -24,7 +59,7 @@ jobs:
|
||||
SDK: "10.0.18362.0"
|
||||
VC: "call vcvars32.bat && cd Libraries"
|
||||
GIT: "https://github.com"
|
||||
QT: "5_12_5"
|
||||
QT: "5_12_8"
|
||||
OPENSSL_VER: "1_1_1"
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
ONLY_CACHE: "false"
|
||||
@@ -76,15 +111,6 @@ jobs:
|
||||
run: |
|
||||
choco install --no-progress -y nasm yasm jom ninja
|
||||
|
||||
- name: Range-v3.
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Find necessary branch from doc."
|
||||
cloneRange=$(grep -A 1 "range-v3" $REPO_NAME/$DOC_PATH | sed -n 1p)
|
||||
cd $LibrariesPath
|
||||
echo $cloneRange
|
||||
eval $cloneRange
|
||||
|
||||
- name: Patches.
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -263,13 +289,13 @@ jobs:
|
||||
|
||||
rmdir /S /Q .git
|
||||
|
||||
- name: Qt 5.12.5 cache.
|
||||
- name: Qt 5.12.8 cache.
|
||||
id: cache-qt
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/Qt-5.12.5
|
||||
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_5_12_5.diff') }}
|
||||
- name: Configure Qt 5.12.5.
|
||||
path: ${{ env.LibrariesPath }}/Qt-5.12.8
|
||||
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_5_12_8.diff') }}
|
||||
- name: Configure Qt 5.12.8.
|
||||
if: steps.cache-qt.outputs.cache-hit != 'true'
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -278,18 +304,18 @@ jobs:
|
||||
git clone git://code.qt.io/qt/qt5.git qt_%QT%
|
||||
cd qt_%QT%
|
||||
perl init-repository --module-subset=qtbase,qtimageformats
|
||||
git checkout v5.12.5
|
||||
git checkout v5.12.8
|
||||
git submodule update qtbase
|
||||
git submodule update qtimageformats
|
||||
cd qtbase
|
||||
git apply ../../patches/qtbase_%QT%.diff
|
||||
for /r %%i in (..\..\patches\qtbase_%QT%\*) do git apply %%i
|
||||
cd ..
|
||||
|
||||
SET SSL=%LibrariesPath%\openssl_1_1_1
|
||||
SET LIBS=libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib
|
||||
|
||||
configure ^
|
||||
-prefix "%LibrariesPath%\Qt-5.12.5" ^
|
||||
-prefix "%LibrariesPath%\Qt-5.12.8" ^
|
||||
-debug ^
|
||||
-force-debug-info ^
|
||||
-opensource ^
|
||||
@@ -304,7 +330,7 @@ jobs:
|
||||
-nomake examples ^
|
||||
-nomake tests ^
|
||||
-platform win32-msvc
|
||||
- name: Qt 5.12.5 build.
|
||||
- name: Qt 5.12.8 build.
|
||||
if: steps.cache-qt.outputs.cache-hit != 'true'
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -324,6 +350,9 @@ jobs:
|
||||
if [ -n "${{ matrix.defines }}" ]; then
|
||||
DEFINE="-D ${{ matrix.defines }}=ON"
|
||||
echo Define from matrix: $DEFINE
|
||||
echo ::set-env name=ARTIFACT_NAME::Telegram_${{ matrix.defines }}
|
||||
else
|
||||
echo ::set-env name=ARTIFACT_NAME::Telegram
|
||||
fi
|
||||
echo "::set-env name=TDESKTOP_BUILD_DEFINE::$DEFINE"
|
||||
|
||||
@@ -355,5 +384,5 @@ jobs:
|
||||
name: Upload artifact.
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
with:
|
||||
name: Telegram
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: ${{ env.REPO_NAME }}\out\Debug\artifact\
|
||||
|
||||
29
.gitmodules
vendored
@@ -3,7 +3,7 @@
|
||||
url = https://github.com/telegramdesktop/libtgvoip
|
||||
[submodule "Telegram/ThirdParty/variant"]
|
||||
path = Telegram/ThirdParty/variant
|
||||
url = https://github.com/mapbox/variant
|
||||
url = https://github.com/desktop-app/variant.git
|
||||
[submodule "Telegram/ThirdParty/GSL"]
|
||||
path = Telegram/ThirdParty/GSL
|
||||
url = https://github.com/Microsoft/GSL.git
|
||||
@@ -67,3 +67,30 @@
|
||||
[submodule "Telegram/ThirdParty/hunspell"]
|
||||
path = Telegram/ThirdParty/hunspell
|
||||
url = https://github.com/hunspell/hunspell
|
||||
[submodule "Telegram/ThirdParty/materialdecoration"]
|
||||
path = Telegram/ThirdParty/materialdecoration
|
||||
url = https://github.com/desktop-app/materialdecoration.git
|
||||
[submodule "Telegram/ThirdParty/range-v3"]
|
||||
path = Telegram/ThirdParty/range-v3
|
||||
url = https://github.com/ericniebler/range-v3.git
|
||||
[submodule "Telegram/ThirdParty/fcitx-qt5"]
|
||||
path = Telegram/ThirdParty/fcitx-qt5
|
||||
url = https://github.com/fcitx/fcitx-qt5.git
|
||||
[submodule "Telegram/ThirdParty/nimf"]
|
||||
path = Telegram/ThirdParty/nimf
|
||||
url = https://github.com/hamonikr/nimf.git
|
||||
[submodule "Telegram/ThirdParty/hime"]
|
||||
path = Telegram/ThirdParty/hime
|
||||
url = https://github.com/hime-ime/hime.git
|
||||
[submodule "Telegram/ThirdParty/qt5ct"]
|
||||
path = Telegram/ThirdParty/qt5ct
|
||||
url = https://github.com/desktop-app/qt5ct.git
|
||||
[submodule "Telegram/ThirdParty/lxqt-qtplugin"]
|
||||
path = Telegram/ThirdParty/lxqt-qtplugin
|
||||
url = https://github.com/lxqt/lxqt-qtplugin.git
|
||||
[submodule "Telegram/ThirdParty/libqtxdg"]
|
||||
path = Telegram/ThirdParty/libqtxdg
|
||||
url = https://github.com/lxqt/libqtxdg.git
|
||||
[submodule "Telegram/ThirdParty/fcitx5-qt"]
|
||||
path = Telegram/ThirdParty/fcitx5-qt
|
||||
url = https://github.com/fcitx/fcitx5-qt.git
|
||||
|
||||
28
README.md
@@ -13,18 +13,27 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
|
||||
|
||||
## Supported systems
|
||||
|
||||
* Windows XP - Windows 10 (**not** RT)
|
||||
* Mac OS X 10.8 - Mac OS X 10.15
|
||||
* Mac OS X 10.6 - Mac OS X 10.7 (separate build)
|
||||
* Ubuntu 12.04 - Ubuntu 19.10
|
||||
* Fedora 22 - Fedora 31
|
||||
* [Snappy](https://snapcraft.io/telegram-desktop)
|
||||
* [Flathub](https://flathub.org/apps/details/org.telegram.desktop)
|
||||
The latest version is available for
|
||||
|
||||
* [Windows 7 and above](https://telegram.org/dl/desktop/win) ([portable](https://telegram.org/dl/desktop/win_portable))
|
||||
* [macOS 10.12 and above](https://telegram.org/dl/desktop/mac)
|
||||
* [OS X 10.10 and 10.11](https://telegram.org/dl/desktop/osx)
|
||||
* [Linux static build for 64 bit](https://telegram.org/dl/desktop/linux) ([32 bit](https://telegram.org/dl/desktop/linux32))
|
||||
* [Snap](https://snapcraft.io/telegram-desktop)
|
||||
* [Flatpak](https://flathub.org/apps/details/org.telegram.desktop)
|
||||
|
||||
## Old system versions
|
||||
|
||||
Version **1.8.15** was the last that supports older systems
|
||||
|
||||
* [Windows XP and Vista](https://updates.tdesktop.com/tsetup/tsetup.1.8.15.exe) ([portable](https://updates.tdesktop.com/tsetup/tportable.1.8.15.zip))
|
||||
* [OS X 10.8 and 10.9](https://updates.tdesktop.com/tmac/tsetup.1.8.15.dmg)
|
||||
* [OS X 10.6 and 10.7](https://updates.tdesktop.com/tmac32/tsetup32.1.8.15.dmg)
|
||||
|
||||
## Third-party
|
||||
|
||||
* Qt 5.12.5 and 5.6.2, slightly patched ([LGPL](http://doc.qt.io/qt-5/lgpl.html))
|
||||
* OpenSSL 1.1.1 ([OpenSSL License](https://www.openssl.org/source/license.html))
|
||||
* Qt 5.12.8, 5.6.2 and 5.3.2 slightly patched ([LGPL](http://doc.qt.io/qt-5/lgpl.html))
|
||||
* OpenSSL 1.1.1 and 1.0.1 ([OpenSSL License](https://www.openssl.org/source/license.html))
|
||||
* zlib 1.2.11 ([zlib License](http://www.zlib.net/zlib_license.html))
|
||||
* LZMA SDK 9.20 ([public domain](http://www.7-zip.org/sdk.html))
|
||||
* liblzma ([public domain](http://tukaani.org/xz/))
|
||||
@@ -39,6 +48,7 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
|
||||
* Mapbox Variant ([BSD License](https://github.com/mapbox/variant/blob/master/LICENSE))
|
||||
* Range-v3 ([Boost License](https://github.com/ericniebler/range-v3/blob/master/LICENSE.txt))
|
||||
* Open Sans font ([Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html))
|
||||
* Vazir font ([License](https://github.com/rastikerdar/vazir-font/blob/master/LICENSE))
|
||||
* Emoji alpha codes ([MIT License](https://github.com/emojione/emojione/blob/master/extras/alpha-codes/LICENSE.md))
|
||||
* Catch test framework ([Boost License](https://github.com/philsquared/Catch/blob/master/LICENSE.txt))
|
||||
* xxHash ([BSD License](https://github.com/Cyan4973/xxHash/blob/dev/LICENSE))
|
||||
|
||||
@@ -67,12 +67,34 @@ generate_numbers(Telegram ${res_loc}/numbers.txt)
|
||||
|
||||
set_target_properties(Telegram PROPERTIES AUTOMOC ON AUTORCC ON)
|
||||
|
||||
if (LINUX AND NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
|
||||
if (LINUX)
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
desktop-app::external_statusnotifieritem
|
||||
desktop-app::external_dbusmenu_qt
|
||||
desktop-app::external_materialdecoration
|
||||
desktop-app::external_nimf_qt5
|
||||
desktop-app::external_qt5ct
|
||||
desktop-app::external_qt5ct_style
|
||||
desktop-app::external_qt5ct_qtplugin
|
||||
)
|
||||
|
||||
if (NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
|
||||
# conflicts with Qt static link
|
||||
if (DESKTOP_APP_USE_PACKAGED_LAZY_PLATFORMTHEMES)
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
desktop-app::external_lxqt_qtplugin
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
desktop-app::external_statusnotifieritem
|
||||
desktop-app::external_dbusmenu_qt
|
||||
desktop-app::external_fcitx_qt5
|
||||
desktop-app::external_fcitx5_qt5
|
||||
desktop-app::external_hime_qt
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (add_hunspell_library)
|
||||
@@ -105,10 +127,6 @@ PRIVATE
|
||||
desktop-app::external_openal
|
||||
)
|
||||
|
||||
if (NOT DESKTOP_APP_USE_PACKAGED)
|
||||
target_link_libraries(Telegram PRIVATE desktop-app::external_opus)
|
||||
endif()
|
||||
|
||||
# Telegram uses long atomic types, so on some architectures libatomic is needed.
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
@@ -121,11 +139,10 @@ endif()
|
||||
|
||||
if (DESKTOP_APP_USE_PACKAGED)
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
find_package(Threads)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
${CMAKE_DL_LIBS}
|
||||
Threads::Threads
|
||||
)
|
||||
endif()
|
||||
@@ -247,6 +264,9 @@ PRIVATE
|
||||
calls/calls_box_controller.h
|
||||
calls/calls_call.cpp
|
||||
calls/calls_call.h
|
||||
calls/calls_controller.cpp
|
||||
calls/calls_controller.h
|
||||
calls/calls_controller_tgvoip.h
|
||||
calls/calls_emoji_fingerprint.cpp
|
||||
calls/calls_emoji_fingerprint.h
|
||||
calls/calls_instance.cpp
|
||||
@@ -281,6 +301,8 @@ PRIVATE
|
||||
chat_helpers/stickers_dice_pack.h
|
||||
chat_helpers/stickers_list_widget.cpp
|
||||
chat_helpers/stickers_list_widget.h
|
||||
chat_helpers/stickers_set.cpp
|
||||
chat_helpers/stickers_set.h
|
||||
chat_helpers/tabbed_panel.cpp
|
||||
chat_helpers/tabbed_panel.h
|
||||
chat_helpers/tabbed_section.cpp
|
||||
@@ -309,7 +331,6 @@ PRIVATE
|
||||
core/launcher.h
|
||||
core/local_url_handlers.cpp
|
||||
core/local_url_handlers.h
|
||||
core/media_active_cache.h
|
||||
core/mime_type.cpp
|
||||
core/mime_type.h
|
||||
core/sandbox.cpp
|
||||
@@ -335,14 +356,16 @@ PRIVATE
|
||||
data/data_channel.h
|
||||
data/data_channel_admins.cpp
|
||||
data/data_channel_admins.h
|
||||
data/data_cloud_file.cpp
|
||||
data/data_cloud_file.h
|
||||
data/data_cloud_themes.cpp
|
||||
data/data_cloud_themes.h
|
||||
data/data_countries.cpp
|
||||
data/data_countries.h
|
||||
data/data_document.cpp
|
||||
data/data_document.h
|
||||
data/data_document_good_thumbnail.cpp
|
||||
data/data_document_good_thumbnail.h
|
||||
data/data_document_media.cpp
|
||||
data/data_document_media.h
|
||||
data/data_drafts.cpp
|
||||
data/data_drafts.h
|
||||
data/data_folder.cpp
|
||||
@@ -373,10 +396,14 @@ PRIVATE
|
||||
data/data_peer_values.h
|
||||
data/data_photo.cpp
|
||||
data/data_photo.h
|
||||
data/data_photo_media.cpp
|
||||
data/data_photo_media.h
|
||||
data/data_poll.cpp
|
||||
data/data_poll.h
|
||||
data/data_pts_waiter.cpp
|
||||
data/data_pts_waiter.h
|
||||
data/data_reply_preview.cpp
|
||||
data/data_reply_preview.h
|
||||
data/data_search_controller.cpp
|
||||
data/data_search_controller.h
|
||||
data/data_session.cpp
|
||||
@@ -737,6 +764,7 @@ PRIVATE
|
||||
mtproto/type_utils.h
|
||||
overview/overview_layout.cpp
|
||||
overview/overview_layout.h
|
||||
overview/overview_layout_delegate.h
|
||||
passport/passport_encryption.cpp
|
||||
passport/passport_encryption.h
|
||||
passport/passport_form_controller.cpp
|
||||
@@ -779,7 +807,6 @@ PRIVATE
|
||||
platform/mac/file_utilities_mac.h
|
||||
platform/mac/launcher_mac.mm
|
||||
platform/mac/launcher_mac.h
|
||||
platform/mac/mac_iconv_helper.c
|
||||
platform/mac/main_window_mac.mm
|
||||
platform/mac/main_window_mac.h
|
||||
platform/mac/notifications_manager_mac.mm
|
||||
@@ -906,8 +933,8 @@ PRIVATE
|
||||
ui/image/image.h
|
||||
ui/image/image_location.cpp
|
||||
ui/image/image_location.h
|
||||
ui/image/image_source.cpp
|
||||
ui/image/image_source.h
|
||||
ui/image/image_location_factory.cpp
|
||||
ui/image/image_location_factory.h
|
||||
ui/widgets/continuous_sliders.cpp
|
||||
ui/widgets/continuous_sliders.h
|
||||
ui/widgets/discrete_sliders.cpp
|
||||
@@ -1010,6 +1037,7 @@ PRIVATE
|
||||
mainwindow.h
|
||||
observer_peer.cpp
|
||||
observer_peer.h
|
||||
qt_static_plugins.cpp
|
||||
settings.cpp
|
||||
settings.h
|
||||
)
|
||||
@@ -1017,7 +1045,7 @@ PRIVATE
|
||||
if (DESKTOP_APP_USE_PACKAGED)
|
||||
nice_target_sources(Telegram ${src_loc} PRIVATE qt_functions.cpp)
|
||||
else()
|
||||
nice_target_sources(Telegram ${src_loc} PRIVATE qt_static_plugins.cpp)
|
||||
nice_target_sources(Telegram ${src_loc} PRIVATE platform/mac/mac_iconv_helper.c)
|
||||
endif()
|
||||
|
||||
nice_target_sources(Telegram ${res_loc}
|
||||
@@ -1027,12 +1055,15 @@ PRIVATE
|
||||
qrc/emoji_3.qrc
|
||||
qrc/emoji_4.qrc
|
||||
qrc/emoji_5.qrc
|
||||
qrc/emoji_6.qrc
|
||||
qrc/emoji_7.qrc
|
||||
qrc/emoji_preview.qrc
|
||||
qrc/telegram/telegram.qrc
|
||||
qrc/telegram/sounds.qrc
|
||||
winrc/Telegram.rc
|
||||
winrc/Telegram.manifest
|
||||
langs/lang.strings
|
||||
langs/cloud_lang.strings
|
||||
numbers.txt
|
||||
)
|
||||
|
||||
@@ -1050,11 +1081,11 @@ if (WIN32)
|
||||
# $<IF:${release},"Appending compatibility manifest.","Finalizing build.">
|
||||
# )
|
||||
elseif (APPLE)
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
desktop-app::external_sp_media_key_tap
|
||||
desktop-app::external_iconv
|
||||
)
|
||||
target_link_libraries(Telegram PRIVATE desktop-app::external_sp_media_key_tap)
|
||||
|
||||
if (NOT DESKTOP_APP_USE_PACKAGED)
|
||||
target_link_libraries(Telegram PRIVATE desktop-app::external_iconv)
|
||||
endif()
|
||||
|
||||
set(icons_path ${CMAKE_CURRENT_SOURCE_DIR}/Telegram/Images.xcassets)
|
||||
set_target_properties(Telegram PROPERTIES RESOURCE ${icons_path})
|
||||
@@ -1162,21 +1193,12 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/Telegram PREFIX Resources FILES ${
|
||||
|
||||
target_include_directories(Telegram PRIVATE ${src_loc})
|
||||
|
||||
if (NOT DESKTOP_APP_USE_PACKAGED)
|
||||
target_include_directories(Telegram PRIVATE ${third_party_loc}/minizip)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(Telegram
|
||||
PRIVATE
|
||||
TDESKTOP_API_ID=${TDESKTOP_API_ID}
|
||||
TDESKTOP_API_HASH=${TDESKTOP_API_HASH}
|
||||
AL_ALEXT_PROTOTYPES
|
||||
)
|
||||
|
||||
if (NOT DESKTOP_APP_USE_PACKAGED)
|
||||
target_compile_definitions(Telegram PRIVATE AL_LIBTYPE_STATIC)
|
||||
endif()
|
||||
|
||||
if (${CMAKE_GENERATOR} MATCHES "(Visual Studio|Xcode)")
|
||||
set(output_folder ${CMAKE_BINARY_DIR})
|
||||
elseif (DESKTOP_APP_SPECIAL_TARGET STREQUAL "")
|
||||
@@ -1187,7 +1209,7 @@ endif()
|
||||
|
||||
set_target_properties(Telegram PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder})
|
||||
|
||||
if ((NOT disable_autoupdate OR NOT LINUX) AND NOT build_macstore AND NOT build_winstore)
|
||||
if ((NOT DESKTOP_APP_DISABLE_AUTOUPDATE OR NOT LINUX) AND NOT build_macstore AND NOT build_winstore)
|
||||
add_executable(Updater WIN32)
|
||||
init_target(Updater)
|
||||
|
||||
@@ -1203,6 +1225,10 @@ if ((NOT disable_autoupdate OR NOT LINUX) AND NOT build_macstore AND NOT build_w
|
||||
|
||||
set_target_properties(Updater PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder})
|
||||
|
||||
if (WIN32 AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
target_link_options(Updater PRIVATE -municode)
|
||||
endif()
|
||||
|
||||
if (LINUX)
|
||||
target_link_options(Updater PRIVATE -static-libstdc++)
|
||||
endif()
|
||||
|
||||
BIN
Telegram/Resources/art/dart_idle.tgs
Normal file
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 894 KiB After Width: | Height: | Size: 1.4 MiB |
BIN
Telegram/Resources/emoji/emoji_6.webp
Normal file
|
After Width: | Height: | Size: 862 KiB |
BIN
Telegram/Resources/emoji/emoji_7.webp
Normal file
|
After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 763 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
BIN
Telegram/Resources/icons/filters/filters_edit.png
Normal file
|
After Width: | Height: | Size: 339 B |
BIN
Telegram/Resources/icons/filters/filters_edit@2x.png
Normal file
|
After Width: | Height: | Size: 686 B |
BIN
Telegram/Resources/icons/filters/filters_edit@3x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/message_psa_tooltip.png
Normal file
|
After Width: | Height: | Size: 549 B |
BIN
Telegram/Resources/icons/message_psa_tooltip@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/message_psa_tooltip@3x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
Telegram/Resources/icons/quiz_explain.png
Normal file
|
After Width: | Height: | Size: 542 B |
BIN
Telegram/Resources/icons/quiz_explain@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/quiz_explain@3x.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/Resources/icons/quiz_timer.png
Normal file
|
After Width: | Height: | Size: 409 B |
BIN
Telegram/Resources/icons/quiz_timer@2x.png
Normal file
|
After Width: | Height: | Size: 819 B |
BIN
Telegram/Resources/icons/quiz_timer@3x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 173 B After Width: | Height: | Size: 506 B |
|
Before Width: | Height: | Size: 120 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 145 B After Width: | Height: | Size: 1.6 KiB |
BIN
Telegram/Resources/icons/stickers_add_dot.png
Normal file
|
After Width: | Height: | Size: 204 B |
BIN
Telegram/Resources/icons/stickers_add_dot@2x.png
Normal file
|
After Width: | Height: | Size: 342 B |
BIN
Telegram/Resources/icons/stickers_add_dot@3x.png
Normal file
|
After Width: | Height: | Size: 522 B |
BIN
Telegram/Resources/icons/stickers_add_unread.png
Normal file
|
After Width: | Height: | Size: 535 B |
BIN
Telegram/Resources/icons/stickers_add_unread@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/stickers_add_unread@3x.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/Resources/icons/stickers_recent.png
Normal file
|
After Width: | Height: | Size: 527 B |
BIN
Telegram/Resources/icons/stickers_recent@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/stickers_recent@3x.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/Resources/icons/toast_info.png
Normal file
|
After Width: | Height: | Size: 504 B |
BIN
Telegram/Resources/icons/toast_info@2x.png
Normal file
|
After Width: | Height: | Size: 992 B |
BIN
Telegram/Resources/icons/toast_info@3x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
@@ -6,6 +6,11 @@ For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
|
||||
"cloud_lng_badge_psa_covid" = "COVID-19";
|
||||
"cloud_lng_about_psa_covid" = "This message provides you with a public service announcement in relation to the ongoing COVID-19 pandemic. To remove it from your chats list, right click it and select **Hide**.";
|
||||
"cloud_lng_forwarded_psa_covid" = "COVID-19 Notification from {channel}";
|
||||
"cloud_lng_tooltip_psa_covid" = "This message provides you with a public service announcement in relation to the ongoing COVID-19 pandemic. Learn more about this initiative at https://telegram.org/blog/coronavirus";
|
||||
|
||||
"cloud_lng_passport_in_ar" = "Arabic";
|
||||
"cloud_lng_passport_in_az" = "Azerbaijani";
|
||||
"cloud_lng_passport_in_bg" = "Bulgarian";
|
||||
|
||||
@@ -118,6 +118,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_chat_status_online#one" = "{count} online";
|
||||
"lng_chat_status_online#other" = "{count} online";
|
||||
"lng_chat_status_members_online" = "{members_count}, {online_count}";
|
||||
"lng_chat_status_subscribers#one" = "{count} subscriber";
|
||||
"lng_chat_status_subscribers#other" = "{count} subscribers";
|
||||
|
||||
"lng_channel_status" = "channel";
|
||||
"lng_group_status" = "group";
|
||||
@@ -298,6 +300,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_notifications_position" = "Location on the screen";
|
||||
"lng_settings_notifications_count" = "Notifications count";
|
||||
"lng_settings_sound_notify" = "Play sound";
|
||||
"lng_settings_alert_windows" = "Flash the taskbar icon";
|
||||
"lng_settings_alert_mac" = "Bounce the dock icon";
|
||||
"lng_settings_alert_linux" = "Draw attention to the window";
|
||||
"lng_settings_badge_title" = "Badge counter";
|
||||
"lng_settings_include_muted" = "Include muted chats in unread count";
|
||||
"lng_settings_count_unread" = "Count unread messages";
|
||||
@@ -600,6 +605,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_proxy_sponsor" = "Proxy sponsor";
|
||||
"lng_proxy_sponsor_about" = "This channel is shown by your proxy server.\nTo remove this channel from your chats list,\ndisable the proxy in Telegram Settings.";
|
||||
"lng_proxy_sponsor_warning" = "This proxy may display a sponsored channel in your chat list. This doesn't reveal any of your Telegram traffic.";
|
||||
"lng_badge_psa_default" = "PSA";
|
||||
"lng_about_psa_default" = "This message provides you with a public service announcement. To remove it from your chats list, right click it and select **Hide**.";
|
||||
"lng_tooltip_psa_default" = "This message provides you with a public service announcement.";
|
||||
|
||||
"lng_settings_blocked_users" = "Blocked users";
|
||||
"lng_settings_no_blocked_users" = "None";
|
||||
@@ -764,6 +772,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_profile_common_groups#one" = "{count} group in common";
|
||||
"lng_profile_common_groups#other" = "{count} groups in common";
|
||||
"lng_profile_participants_section" = "Members";
|
||||
"lng_profile_subscribers_section" = "Subscribers";
|
||||
"lng_profile_mobile_number" = "Mobile:";
|
||||
"lng_profile_username" = "Username:";
|
||||
"lng_profile_link" = "Link:";
|
||||
@@ -885,6 +894,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_manage_channel_info" = "Channel Info";
|
||||
"lng_manage_peer_recent_actions" = "Recent Actions";
|
||||
"lng_manage_peer_members" = "Members";
|
||||
"lng_manage_peer_subscribers" = "Subscribers";
|
||||
"lng_manage_peer_administrators" = "Administrators";
|
||||
"lng_manage_peer_exceptions" = "Exceptions";
|
||||
"lng_manage_peer_removed_users" = "Removed users";
|
||||
@@ -1126,6 +1136,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_forwarded" = "Forwarded from {user}";
|
||||
"lng_forwarded_date" = "Original: {date}";
|
||||
"lng_forwarded_channel" = "Forwarded from {channel}";
|
||||
"lng_forwarded_psa_default" = "Forwarded from {channel}";
|
||||
"lng_forwarded_via" = "Forwarded from {user} via {inline_bot}";
|
||||
"lng_forwarded_channel_via" = "Forwarded from {channel} via {inline_bot}";
|
||||
"lng_forwarded_signed" = "{channel} ({user})";
|
||||
@@ -1329,7 +1340,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_dialogs_skip_archive_in_search" = "Skip results from archive";
|
||||
"lng_dialogs_show_archive_in_search" = "With results from archive";
|
||||
|
||||
"lng_about_dice" = "Send a 🎲 emoji to any chat to get a random number from Telegram.";
|
||||
"lng_about_random" = "Send a {emoji} emoji to any chat to get a random number from Telegram.";
|
||||
"lng_about_random_send" = "Send";
|
||||
|
||||
"lng_open_this_link" = "Open this link?";
|
||||
"lng_open_link" = "Open";
|
||||
@@ -1400,6 +1412,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"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";
|
||||
"lng_context_mark_unread" = "Mark as unread";
|
||||
@@ -1441,6 +1454,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_edit_msg" = "Edit";
|
||||
"lng_context_forward_msg" = "Forward Message";
|
||||
"lng_context_send_now_msg" = "Send now";
|
||||
"lng_context_reschedule" = "Reschedule";
|
||||
"lng_context_delete_msg" = "Delete Message";
|
||||
"lng_context_select_msg" = "Select Message";
|
||||
"lng_context_report_msg" = "Report Message";
|
||||
@@ -1850,6 +1864,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_no_results_search_text" = "No recent actions that contain '{query}' have been found.";
|
||||
"lng_admin_log_no_events_title" = "No actions yet";
|
||||
"lng_admin_log_no_events_text" = "There were no service actions\ntaken by the group's members\nand admins in the last 48 hours.";
|
||||
"lng_admin_log_no_events_text_channel" = "There were no service actions\ntaken by the channels's admins\nin the last 48 hours.";
|
||||
|
||||
"lng_admin_log_empty_text" = "Empty";
|
||||
"lng_admin_log_changed_title_group" = "{from} changed group name to «{title}»";
|
||||
@@ -2135,6 +2150,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_export_option_size_limit" = "Size limit: {size}";
|
||||
"lng_export_header_format" = "Location and format";
|
||||
"lng_export_option_location" = "Download path: {path}";
|
||||
"lng_export_option_format_location" = "Format: {format}, Path: {path}";
|
||||
"lng_export_option_choose_format" = "Choose export format";
|
||||
"lng_export_option_html" = "Human-readable HTML";
|
||||
"lng_export_option_json" = "Machine-readable JSON";
|
||||
"lng_export_limits" = "From: {from}, to: {till}";
|
||||
@@ -2223,6 +2240,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_polls_choose_question" = "Please enter a question.";
|
||||
"lng_polls_choose_answers" = "Please enter at least two options.";
|
||||
"lng_polls_choose_correct" = "Please choose the correct answer.";
|
||||
"lng_polls_solution_title" = "Explanation";
|
||||
"lng_polls_solution_placeholder" = "Add a Comment (Optional)";
|
||||
"lng_polls_solution_about" = "Users will see this comment after choosing a wrong answer, good for educational purposes.";
|
||||
|
||||
"lng_polls_poll_results_title" = "Poll results";
|
||||
"lng_polls_quiz_results_title" = "Quiz results";
|
||||
@@ -2231,6 +2251,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_polls_votes_collapse" = "Collapse";
|
||||
|
||||
"lng_outdated_title" = "PLEASE UPDATE YOUR OPERATING SYSTEM.";
|
||||
"lng_outdated_title_bits" = "PLEASE SWITCH TO 64 BIT OPERATING SYSTEM.";
|
||||
"lng_outdated_soon" = "Otherwise, Telegram Desktop will stop updating on {date}.";
|
||||
"lng_outdated_now" = "So that Telegram Desktop can update to newer versions.";
|
||||
|
||||
|
||||
5
Telegram/Resources/qrc/emoji_6.qrc
Normal file
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/gui">
|
||||
<file alias="emoji/emoji_6.webp">../emoji/emoji_6.webp</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
5
Telegram/Resources/qrc/emoji_7.qrc
Normal file
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/gui">
|
||||
<file alias="emoji/emoji_7.webp">../emoji/emoji_7.webp</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -48,6 +48,7 @@
|
||||
<file alias="art/logo_256_no_margin.png">../../art/logo_256_no_margin.png</file>
|
||||
<file alias="art/sunrise.jpg">../../art/sunrise.jpg</file>
|
||||
<file alias="art/dice_idle.tgs">../../art/dice_idle.tgs</file>
|
||||
<file alias="art/dart_idle.tgs">../../art/dart_idle.tgs</file>
|
||||
<file alias="day-blue.tdesktop-theme">../../day-blue.tdesktop-theme</file>
|
||||
<file alias="night.tdesktop-theme">../../night.tdesktop-theme</file>
|
||||
<file alias="night-green.tdesktop-theme">../../night-green.tdesktop-theme</file>
|
||||
|
||||
@@ -71,8 +71,8 @@ inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int =
|
||||
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
|
||||
inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
|
||||
inputMediaGeoLive#ce4e82fd flags:# stopped:flags.0?true geo_point:InputGeoPoint period:flags.1?int = InputMedia;
|
||||
inputMediaPoll#abe9ca25 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> = InputMedia;
|
||||
inputMediaDice#aeffa807 = InputMedia;
|
||||
inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector<bytes> solution:flags.1?string solution_entities:flags.1?Vector<MessageEntity> = InputMedia;
|
||||
inputMediaDice#e66fbf7b emoticon:string = InputMedia;
|
||||
|
||||
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
|
||||
inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
|
||||
@@ -157,7 +157,7 @@ messageMediaGame#fdb19008 game:Game = MessageMedia;
|
||||
messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia;
|
||||
messageMediaGeoLive#7c3c2609 geo:GeoPoint period:int = MessageMedia;
|
||||
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
|
||||
messageMediaDice#638fe46b value:int = MessageMedia;
|
||||
messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
|
||||
|
||||
messageActionEmpty#b6aef7b0 = MessageAction;
|
||||
messageActionChatCreate#a6638b9a title:string users:Vector<int> = MessageAction;
|
||||
@@ -357,6 +357,7 @@ updateMessagePollVote#42f88f2c poll_id:long user_id:int options:Vector<bytes> =
|
||||
updateDialogFilter#26ffde7d flags:# id:int filter:flags.0?DialogFilter = Update;
|
||||
updateDialogFilterOrder#a5d72105 order:Vector<int> = Update;
|
||||
updateDialogFilters#3504914f = Update;
|
||||
updatePhoneCallSignalingData#2661bf09 phone_call_id:long data:bytes = Update;
|
||||
|
||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||
|
||||
@@ -421,7 +422,7 @@ inputDocumentEmpty#72f0eaae = InputDocument;
|
||||
inputDocument#1abfb575 id:long access_hash:long file_reference:bytes = InputDocument;
|
||||
|
||||
documentEmpty#36f8c871 id:long = Document;
|
||||
document#9ba29cc1 flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector<PhotoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
|
||||
document#1e87342b flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector<PhotoSize> video_thumbs:flags.1?Vector<VideoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
|
||||
|
||||
help.support#17c6b5f6 phone_number:string user:User = help.Support;
|
||||
|
||||
@@ -533,7 +534,7 @@ inputStickerSetEmpty#ffb62b95 = InputStickerSet;
|
||||
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
|
||||
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
|
||||
inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
|
||||
inputStickerSetDice#79e21a53 = InputStickerSet;
|
||||
inputStickerSetDice#e67f520e emoticon:string = InputStickerSet;
|
||||
|
||||
stickerSet#eeb46f27 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 thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
|
||||
|
||||
@@ -651,7 +652,7 @@ messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_off
|
||||
|
||||
exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink;
|
||||
|
||||
messageFwdHeader#ec338270 flags:# from_id:flags.0?int from_name:flags.5?string date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int = MessageFwdHeader;
|
||||
messageFwdHeader#353a686b flags:# from_id:flags.0?int from_name:flags.5?string date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int psa_type:flags.6?string = MessageFwdHeader;
|
||||
|
||||
auth.codeTypeSms#72a3158c = auth.CodeType;
|
||||
auth.codeTypeCall#741cd3e3 = auth.CodeType;
|
||||
@@ -692,8 +693,8 @@ contacts.topPeersDisabled#b52c939d = contacts.TopPeers;
|
||||
draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
|
||||
draftMessage#fd8e711f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int message:string entities:flags.3?Vector<MessageEntity> date:int = DraftMessage;
|
||||
|
||||
messages.featuredStickersNotModified#4ede3cf = messages.FeaturedStickers;
|
||||
messages.featuredStickers#f89d88e5 hash:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
|
||||
messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers;
|
||||
messages.featuredStickers#b6abc341 hash:int count:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
|
||||
|
||||
messages.recentStickersNotModified#b17f890 = messages.RecentStickers;
|
||||
messages.recentStickers#22f3afb3 hash:int packs:Vector<StickerPack> stickers:Vector<Document> dates:Vector<int> = messages.RecentStickers;
|
||||
@@ -912,9 +913,6 @@ fileHash#6242c773 offset:int limit:int hash:bytes = FileHash;
|
||||
|
||||
inputClientProxy#75588b3f address:string port:int = InputClientProxy;
|
||||
|
||||
help.proxyDataEmpty#e09e1fb8 expires:int = help.ProxyData;
|
||||
help.proxyDataPromo#2bf7ee23 expires:int peer:Peer chats:Vector<Chat> users:Vector<User> = help.ProxyData;
|
||||
|
||||
help.termsOfServiceUpdateEmpty#e3309f7f expires:int = help.TermsOfServiceUpdate;
|
||||
help.termsOfServiceUpdate#28ecf961 expires:int terms_of_service:help.TermsOfService = help.TermsOfServiceUpdate;
|
||||
|
||||
@@ -1024,11 +1022,11 @@ help.userInfo#1eb3758 message:string entities:Vector<MessageEntity> author:strin
|
||||
|
||||
pollAnswer#6ca9c2e9 text:string option:bytes = PollAnswer;
|
||||
|
||||
poll#d5529d06 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector<PollAnswer> = Poll;
|
||||
poll#86e18161 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector<PollAnswer> close_period:flags.4?int close_date:flags.5?int = Poll;
|
||||
|
||||
pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true correct:flags.1?true option:bytes voters:int = PollAnswerVoters;
|
||||
|
||||
pollResults#c87024a2 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<int> = PollResults;
|
||||
pollResults#badcc1a3 flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int recent_voters:flags.3?Vector<int> solution:flags.4?string solution_entities:flags.4?Vector<MessageEntity> = PollResults;
|
||||
|
||||
chatOnlines#f041e250 onlines:int = ChatOnlines;
|
||||
|
||||
@@ -1140,11 +1138,16 @@ messageInteractionCounters#ad4fc9bd msg_id:int views:int forwards:int = MessageI
|
||||
|
||||
stats.broadcastStats#bdf78394 period:StatsDateRangeDays followers:StatsAbsValueAndPrev views_per_post:StatsAbsValueAndPrev shares_per_post:StatsAbsValueAndPrev enabled_notifications:StatsPercentValue growth_graph:StatsGraph followers_graph:StatsGraph mute_graph:StatsGraph top_hours_graph:StatsGraph interactions_graph:StatsGraph iv_interactions_graph:StatsGraph views_by_source_graph:StatsGraph new_followers_by_source_graph:StatsGraph languages_graph:StatsGraph recent_message_interactions:Vector<MessageInteractionCounters> = stats.BroadcastStats;
|
||||
|
||||
help.promoDataEmpty#98f6ac75 expires:int = help.PromoData;
|
||||
help.promoData#8c39793f flags:# proxy:flags.0?true expires:int peer:Peer chats:Vector<Chat> users:Vector<User> psa_type:flags.1?string psa_message:flags.2?string = help.PromoData;
|
||||
|
||||
videoSize#435bb987 type:string location:FileLocation w:int h:int size:int = VideoSize;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector<long> query:!X = X;
|
||||
initConnection#785188b8 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy query:!X = X;
|
||||
initConnection#c1cd5ea9 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy params:flags.1?JSONValue query:!X = X;
|
||||
invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
|
||||
invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
|
||||
invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
|
||||
@@ -1382,6 +1385,7 @@ messages.getDialogFilters#f19ed96d = Vector<DialogFilter>;
|
||||
messages.getSuggestedDialogFilters#a29cd42c = Vector<DialogFilterSuggested>;
|
||||
messages.updateDialogFilter#1ad4a04a flags:# id:int filter:flags.0?DialogFilter = Bool;
|
||||
messages.updateDialogFiltersOrder#c563c1e4 order:Vector<int> = Bool;
|
||||
messages.getOldFeaturedStickers#5fe7025b offset:int limit:int hash:int = messages.FeaturedStickers;
|
||||
|
||||
updates.getState#edd4882a = updates.State;
|
||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||
@@ -1410,7 +1414,6 @@ help.getAppChangelog#9010ef6f prev_app_version:string = Updates;
|
||||
help.setBotUpdatesStatus#ec22cfcd pending_updates_count:int message:string = Bool;
|
||||
help.getCdnConfig#52029342 = CdnConfig;
|
||||
help.getRecentMeUrls#3dc0f114 referer:string = help.RecentMeUrls;
|
||||
help.getProxyData#3d7758e1 = help.ProxyData;
|
||||
help.getTermsOfServiceUpdate#2ca51fd1 = help.TermsOfServiceUpdate;
|
||||
help.acceptTermsOfService#ee72f79a id:DataJSON = Bool;
|
||||
help.getDeepLinkInfo#3fedc75f path:string = help.DeepLinkInfo;
|
||||
@@ -1420,6 +1423,8 @@ help.getPassportConfig#c661ad08 hash:int = help.PassportConfig;
|
||||
help.getSupportName#d360e72c = help.SupportName;
|
||||
help.getUserInfo#38a08d3 user_id:InputUser = help.UserInfo;
|
||||
help.editUserInfo#66b91b70 user_id:InputUser message:string entities:Vector<MessageEntity> = help.UserInfo;
|
||||
help.getPromoData#c0977421 = help.PromoData;
|
||||
help.hidePromoData#1e251c95 peer:InputPeer = Bool;
|
||||
|
||||
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
|
||||
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
|
||||
@@ -1459,6 +1464,7 @@ channels.getInactiveChannels#11e831ee = messages.InactiveChats;
|
||||
|
||||
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
||||
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
||||
bots.setBotCommands#805d46f6 commands:Vector<BotCommand> = Bool;
|
||||
|
||||
payments.getPaymentForm#99f09745 msg_id:int = payments.PaymentForm;
|
||||
payments.getPaymentReceipt#a092a980 msg_id:int = payments.PaymentReceipt;
|
||||
@@ -1468,10 +1474,11 @@ payments.getSavedInfo#227d824b = payments.SavedInfo;
|
||||
payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?true = Bool;
|
||||
payments.getBankCardData#2e79d779 number:string = payments.BankCardData;
|
||||
|
||||
stickers.createStickerSet#9bd86e6a flags:# masks:flags.0?true user_id:InputUser title:string short_name:string stickers:Vector<InputStickerSetItem> = messages.StickerSet;
|
||||
stickers.createStickerSet#f1036780 flags:# masks:flags.0?true animated:flags.1?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector<InputStickerSetItem> = messages.StickerSet;
|
||||
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;
|
||||
stickers.changeStickerPosition#ffb6d4ca sticker:InputDocument position:int = messages.StickerSet;
|
||||
stickers.addStickerToSet#8653febe stickerset:InputStickerSet sticker:InputStickerSetItem = messages.StickerSet;
|
||||
stickers.setStickerSetThumb#9a364e30 stickerset:InputStickerSet thumb:InputDocument = messages.StickerSet;
|
||||
|
||||
phone.getCallConfig#55451fa9 = DataJSON;
|
||||
phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
|
||||
@@ -1481,6 +1488,7 @@ phone.receivedCall#17d54f61 peer:InputPhoneCall = Bool;
|
||||
phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates;
|
||||
phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates;
|
||||
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
|
||||
phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool;
|
||||
|
||||
langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference;
|
||||
langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>;
|
||||
@@ -1494,4 +1502,4 @@ folders.deleteFolder#1c295881 folder_id:int = Updates;
|
||||
stats.getBroadcastStats#ab42441a flags:# dark:flags.0?true channel:InputChannel = stats.BroadcastStats;
|
||||
stats.loadAsyncGraph#621d5fa0 flags:# token:string x:flags.0?long = StatsGraph;
|
||||
|
||||
// LAYER 111
|
||||
// LAYER 114
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="2.0.1.0" />
|
||||
Version="2.1.8.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName>
|
||||
|
||||
@@ -6,7 +6,18 @@
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
#if defined(__MINGW64__) || defined(__MINGW32__)
|
||||
// MinGW-w64, MinGW
|
||||
#if defined(__has_include) && __has_include(<winres.h>)
|
||||
#include <winres.h>
|
||||
#else
|
||||
#include <afxres.h>
|
||||
#include <winresrc.h>
|
||||
#endif
|
||||
#else
|
||||
// MSVC, Windows SDK
|
||||
#include <winres.h>
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
@@ -33,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,0,1,0
|
||||
PRODUCTVERSION 2,0,1,0
|
||||
FILEVERSION 2,1,8,0
|
||||
PRODUCTVERSION 2,1,8,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -51,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "2.0.1.0"
|
||||
VALUE "FileVersion", "2.1.8.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.0.1.0"
|
||||
VALUE "ProductVersion", "2.1.8.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -6,7 +6,18 @@
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
#if defined(__MINGW64__) || defined(__MINGW32__)
|
||||
// MinGW-w64, MinGW
|
||||
#if defined(__has_include) && __has_include(<winres.h>)
|
||||
#include <winres.h>
|
||||
#else
|
||||
#include <afxres.h>
|
||||
#include <winresrc.h>
|
||||
#endif
|
||||
#else
|
||||
// MSVC, Windows SDK
|
||||
#include <winres.h>
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
@@ -24,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,0,1,0
|
||||
PRODUCTVERSION 2,0,1,0
|
||||
FILEVERSION 2,1,8,0
|
||||
PRODUCTVERSION 2,1,8,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -42,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "2.0.1.0"
|
||||
VALUE "FileVersion", "2.1.8.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.0.1.0"
|
||||
VALUE "ProductVersion", "2.1.8.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -268,7 +268,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Compression start, size: " << resultSize << "\n";
|
||||
|
||||
QByteArray compressed, resultCheck;
|
||||
#ifdef Q_OS_WIN // use Lzma SDK for win
|
||||
#if defined Q_OS_WIN && !defined DESKTOP_APP_USE_PACKAGED // use Lzma SDK for win
|
||||
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
|
||||
|
||||
compressed.resize(hSize + resultSize + 1024 * 1024); // rsa signature + sha1 + lzma props + max compressed size
|
||||
|
||||
@@ -27,7 +27,7 @@ extern "C" {
|
||||
#include <openssl/evp.h>
|
||||
} // extern "C"
|
||||
|
||||
#ifdef Q_OS_WIN // use Lzma SDK for win
|
||||
#if defined Q_OS_WIN && !defined DESKTOP_APP_USE_PACKAGED // use Lzma SDK for win
|
||||
#include <LzmaLib.h>
|
||||
#else
|
||||
#include <lzma.h>
|
||||
|
||||
@@ -90,7 +90,7 @@ int main(int argc, const char * argv[]) {
|
||||
|
||||
openLog();
|
||||
pid_t procId = 0;
|
||||
BOOL update = YES, toSettings = NO, autoStart = NO, startInTray = NO, testMode = NO, externalUpdater = NO;
|
||||
BOOL update = YES, toSettings = NO, autoStart = NO, startInTray = NO, testMode = NO, freeType = NO, externalUpdater = NO;
|
||||
BOOL customWorkingDir = NO;
|
||||
NSString *key = nil;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
@@ -116,6 +116,8 @@ int main(int argc, const char * argv[]) {
|
||||
startInTray = YES;
|
||||
} else if ([@"-testmode" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
|
||||
testMode = YES;
|
||||
} else if ([@"-freetype" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
|
||||
freeType = YES;
|
||||
} else if ([@"-externalupdater" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
|
||||
externalUpdater = YES;
|
||||
} else if ([@"-workdir_custom" isEqualToString:[NSString stringWithUTF8String:argv[i]]]) {
|
||||
@@ -255,6 +257,7 @@ int main(int argc, const char * argv[]) {
|
||||
if (_debug) [args addObject:@"-debug"];
|
||||
if (startInTray) [args addObject:@"-startintray"];
|
||||
if (testMode) [args addObject:@"-testmode"];
|
||||
if (freeType) [args addObject:@"-freetype"];
|
||||
if (externalUpdater) [args addObject:@"-externalupdater"];
|
||||
if (autoStart) [args addObject:@"-autostart"];
|
||||
if (key) {
|
||||
|
||||
@@ -339,7 +339,7 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdPara
|
||||
LPWSTR *args;
|
||||
int argsCount;
|
||||
|
||||
bool needupdate = false, autostart = false, debug = false, writeprotected = false, startintray = false, testmode = false, externalupdater = false;
|
||||
bool needupdate = false, autostart = false, debug = false, writeprotected = false, startintray = false, testmode = false, freetype = false, externalupdater = false;
|
||||
args = CommandLineToArgvW(GetCommandLine(), &argsCount);
|
||||
if (args) {
|
||||
for (int i = 1; i < argsCount; ++i) {
|
||||
@@ -355,6 +355,8 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdPara
|
||||
startintray = true;
|
||||
} else if (equal(args[i], L"-testmode")) {
|
||||
testmode = true;
|
||||
} else if (equal(args[i], L"-freetype")) {
|
||||
freetype = true;
|
||||
} else if (equal(args[i], L"-externalupdater")) {
|
||||
externalupdater = true;
|
||||
} else if (equal(args[i], L"-writeprotected") && ++i < argsCount) {
|
||||
@@ -427,6 +429,7 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPWSTR cmdPara
|
||||
if (debug) targs += L" -debug";
|
||||
if (startintray) targs += L" -startintray";
|
||||
if (testmode) targs += L" -testmode";
|
||||
if (freetype) targs += L" -freetype";
|
||||
if (externalupdater) targs += L" -externalupdater";
|
||||
if (!customWorkingDir.empty()) {
|
||||
targs += L" -workdir \"" + customWorkingDir + L"\"";
|
||||
|
||||
@@ -21,6 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "chat_helpers/message_field.h" // ConvertTextTagsToEntities.
|
||||
#include "ui/text/text_entity.h" // TextWithEntities.
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "mainwidget.h"
|
||||
#include "apiwrap.h"
|
||||
#include "app.h"
|
||||
@@ -28,6 +30,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
void InnerFillMessagePostFlags(
|
||||
const Api::SendOptions &options,
|
||||
not_null<PeerData*> peer,
|
||||
MTPDmessage::Flags &flags) {
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
if (!channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_from_id;
|
||||
return;
|
||||
}
|
||||
flags |= MTPDmessage::Flag::f_post;
|
||||
// Don't display views and author of a new post when it's scheduled.
|
||||
if (options.scheduled) {
|
||||
return;
|
||||
}
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
if (peer->asChannel()->addsSignature()) {
|
||||
flags |= MTPDmessage::Flag::f_post_author;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename MediaData>
|
||||
void SendExistingMedia(
|
||||
Api::MessageToSend &&message,
|
||||
@@ -58,15 +80,7 @@ void SendExistingMedia(
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto silentPost = message.action.options.silent
|
||||
|| (channelPost && session->data().notifySilentPosts(peer));
|
||||
if (channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
flags |= MTPDmessage::Flag::f_post;
|
||||
}
|
||||
if (!channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_from_id;
|
||||
} else if (peer->asChannel()->addsSignature()) {
|
||||
flags |= MTPDmessage::Flag::f_post_author;
|
||||
}
|
||||
InnerFillMessagePostFlags(message.action.options, peer, flags);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
@@ -199,8 +213,23 @@ void SendExistingPhoto(
|
||||
}
|
||||
|
||||
bool SendDice(Api::MessageToSend &message) {
|
||||
static const auto kDiceString = QString::fromUtf8("\xF0\x9F\x8E\xB2");
|
||||
if (message.textWithTags.text.midRef(0).trimmed() != kDiceString) {
|
||||
const auto full = message.textWithTags.text.midRef(0).trimmed();
|
||||
auto length = 0;
|
||||
if (!Ui::Emoji::Find(full.data(), full.data() + full.size(), &length)
|
||||
|| length != full.size()) {
|
||||
return false;
|
||||
}
|
||||
auto &account = message.action.history->session().account();
|
||||
auto &config = account.appConfig();
|
||||
static const auto hardcoded = std::vector<QString>{
|
||||
QString::fromUtf8("\xF0\x9F\x8E\xB2"),
|
||||
QString::fromUtf8("\xF0\x9F\x8E\xAF")
|
||||
};
|
||||
const auto list = config.get<std::vector<QString>>(
|
||||
"emojies_send_dice",
|
||||
hardcoded);
|
||||
const auto emoji = full.toString();
|
||||
if (!ranges::contains(list, emoji)) {
|
||||
return false;
|
||||
}
|
||||
const auto history = message.action.history;
|
||||
@@ -229,15 +258,7 @@ bool SendDice(Api::MessageToSend &message) {
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto silentPost = message.action.options.silent
|
||||
|| (channelPost && session->data().notifySilentPosts(peer));
|
||||
if (channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
flags |= MTPDmessage::Flag::f_post;
|
||||
}
|
||||
if (!channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_from_id;
|
||||
} else if (peer->asChannel()->addsSignature()) {
|
||||
flags |= MTPDmessage::Flag::f_post_author;
|
||||
}
|
||||
InnerFillMessagePostFlags(message.action.options, peer, flags);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
@@ -266,7 +287,7 @@ bool SendDice(Api::MessageToSend &message) {
|
||||
MTP_int(HistoryItem::NewMessageDate(
|
||||
message.action.options.scheduled)),
|
||||
MTP_string(),
|
||||
MTP_messageMediaDice(MTP_int(0)),
|
||||
MTP_messageMediaDice(MTP_int(0), MTP_string(emoji)),
|
||||
MTPReplyMarkup(),
|
||||
MTP_vector<MTPMessageEntity>(),
|
||||
MTP_int(1),
|
||||
@@ -284,7 +305,7 @@ bool SendDice(Api::MessageToSend &message) {
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
MTP_int(replyTo),
|
||||
MTP_inputMediaDice(),
|
||||
MTP_inputMediaDice(MTP_string(emoji)),
|
||||
MTP_string(),
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
@@ -303,4 +324,11 @@ bool SendDice(Api::MessageToSend &message) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void FillMessagePostFlags(
|
||||
const Api::SendAction &action,
|
||||
not_null<PeerData*> peer,
|
||||
MTPDmessage::Flags &flags) {
|
||||
InnerFillMessagePostFlags(action.options, peer, flags);
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -23,6 +23,11 @@ void SendExistingPhoto(
|
||||
Api::MessageToSend &&message,
|
||||
not_null<PhotoData*> photo);
|
||||
|
||||
[[nodiscard]] bool SendDice(Api::MessageToSend &message);
|
||||
bool SendDice(Api::MessageToSend &message);
|
||||
|
||||
void FillMessagePostFlags(
|
||||
const SendAction &action,
|
||||
not_null<PeerData*> peer,
|
||||
MTPDmessage::Flags &flags);
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -82,6 +82,7 @@ MTPVector<MTPMessageEntity> EntitiesToMTP(
|
||||
if (entity.length() <= 0) continue;
|
||||
if (option == ConvertOption::SkipLocal
|
||||
&& entity.type() != EntityType::Bold
|
||||
//&& entity.type() != EntityType::Semibold // Not in API.
|
||||
&& entity.type() != EntityType::Italic
|
||||
&& entity.type() != EntityType::Underline
|
||||
&& entity.type() != EntityType::StrikeOut
|
||||
|
||||
@@ -88,8 +88,8 @@ constexpr auto kMaxUsersPerInvite = 100;
|
||||
// that was added to this chat.
|
||||
constexpr auto kForwardMessagesOnAdd = 100;
|
||||
|
||||
constexpr auto kProxyPromotionInterval = TimeId(60 * 60);
|
||||
constexpr auto kProxyPromotionMinDelay = TimeId(10);
|
||||
constexpr auto kTopPromotionInterval = TimeId(60 * 60);
|
||||
constexpr auto kTopPromotionMinDelay = TimeId(10);
|
||||
constexpr auto kSmallDelayMs = 5;
|
||||
constexpr auto kUnreadMentionsPreloadIfLess = 5;
|
||||
constexpr auto kUnreadMentionsFirstRequestLimit = 10;
|
||||
@@ -237,7 +237,7 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||
, _dialogsLoadState(std::make_unique<DialogsLoadState>())
|
||||
, _fileLoader(std::make_unique<TaskQueue>(kFileLoaderQueueStopTimeout))
|
||||
//, _feedReadTimer([=] { readFeeds(); }) // #feed
|
||||
, _proxyPromotionTimer([=] { refreshProxyPromotion(); })
|
||||
, _topPromotionTimer([=] { refreshTopPromotion(); })
|
||||
, _updateNotifySettingsTimer([=] { sendNotifySettingsUpdates(); })
|
||||
, _selfDestruct(std::make_unique<Api::SelfDestruct>(this))
|
||||
, _sensitiveContent(std::make_unique<Api::SensitiveContent>(this)) {
|
||||
@@ -288,13 +288,13 @@ void ApiWrap::requestChangelog(
|
||||
).send();
|
||||
}
|
||||
|
||||
void ApiWrap::refreshProxyPromotion() {
|
||||
void ApiWrap::refreshTopPromotion() {
|
||||
const auto now = base::unixtime::now();
|
||||
const auto next = (_proxyPromotionNextRequestTime != 0)
|
||||
? _proxyPromotionNextRequestTime
|
||||
const auto next = (_topPromotionNextRequestTime != 0)
|
||||
? _topPromotionNextRequestTime
|
||||
: now;
|
||||
if (_proxyPromotionRequestId) {
|
||||
getProxyPromotionDelayed(now, next);
|
||||
if (_topPromotionRequestId) {
|
||||
getTopPromotionDelayed(now, next);
|
||||
return;
|
||||
}
|
||||
const auto key = [&]() -> std::pair<QString, uint32> {
|
||||
@@ -307,51 +307,51 @@ void ApiWrap::refreshProxyPromotion() {
|
||||
}
|
||||
return { proxy.host, proxy.port };
|
||||
}();
|
||||
if (_proxyPromotionKey == key && now < next) {
|
||||
getProxyPromotionDelayed(now, next);
|
||||
if (_topPromotionKey == key && now < next) {
|
||||
getTopPromotionDelayed(now, next);
|
||||
return;
|
||||
}
|
||||
_proxyPromotionKey = key;
|
||||
if (key.first.isEmpty() || !key.second) {
|
||||
proxyPromotionDone(MTP_help_proxyDataEmpty(
|
||||
MTP_int(base::unixtime::now() + kProxyPromotionInterval)));
|
||||
return;
|
||||
}
|
||||
_proxyPromotionRequestId = request(MTPhelp_GetProxyData(
|
||||
)).done([=](const MTPhelp_ProxyData &result) {
|
||||
_proxyPromotionRequestId = 0;
|
||||
proxyPromotionDone(result);
|
||||
_topPromotionKey = key;
|
||||
_topPromotionRequestId = request(MTPhelp_GetPromoData(
|
||||
)).done([=](const MTPhelp_PromoData &result) {
|
||||
_topPromotionRequestId = 0;
|
||||
topPromotionDone(result);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_proxyPromotionRequestId = 0;
|
||||
_topPromotionRequestId = 0;
|
||||
const auto now = base::unixtime::now();
|
||||
const auto next = _proxyPromotionNextRequestTime = now
|
||||
+ kProxyPromotionInterval;
|
||||
if (!_proxyPromotionTimer.isActive()) {
|
||||
getProxyPromotionDelayed(now, next);
|
||||
const auto next = _topPromotionNextRequestTime = now
|
||||
+ kTopPromotionInterval;
|
||||
if (!_topPromotionTimer.isActive()) {
|
||||
getTopPromotionDelayed(now, next);
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::getProxyPromotionDelayed(TimeId now, TimeId next) {
|
||||
_proxyPromotionTimer.callOnce(std::min(
|
||||
std::max(next - now, kProxyPromotionMinDelay),
|
||||
kProxyPromotionInterval) * crl::time(1000));
|
||||
void ApiWrap::getTopPromotionDelayed(TimeId now, TimeId next) {
|
||||
_topPromotionTimer.callOnce(std::min(
|
||||
std::max(next - now, kTopPromotionMinDelay),
|
||||
kTopPromotionInterval) * crl::time(1000));
|
||||
};
|
||||
|
||||
void ApiWrap::proxyPromotionDone(const MTPhelp_ProxyData &proxy) {
|
||||
_proxyPromotionNextRequestTime = proxy.match([&](const auto &data) {
|
||||
void ApiWrap::topPromotionDone(const MTPhelp_PromoData &proxy) {
|
||||
_topPromotionNextRequestTime = proxy.match([&](const auto &data) {
|
||||
return data.vexpires().v;
|
||||
});
|
||||
getProxyPromotionDelayed(base::unixtime::now(), _proxyPromotionNextRequestTime);
|
||||
getTopPromotionDelayed(
|
||||
base::unixtime::now(),
|
||||
_topPromotionNextRequestTime);
|
||||
|
||||
proxy.match([&](const MTPDhelp_proxyDataEmpty &data) {
|
||||
_session->data().setProxyPromoted(nullptr);
|
||||
}, [&](const MTPDhelp_proxyDataPromo &data) {
|
||||
proxy.match([&](const MTPDhelp_promoDataEmpty &data) {
|
||||
_session->data().setTopPromoted(nullptr, QString(), QString());
|
||||
}, [&](const MTPDhelp_promoData &data) {
|
||||
_session->data().processChats(data.vchats());
|
||||
_session->data().processUsers(data.vusers());
|
||||
const auto peerId = peerFromMTP(data.vpeer());
|
||||
const auto peer = _session->data().peer(peerId);
|
||||
_session->data().setProxyPromoted(peer);
|
||||
_session->data().setTopPromoted(
|
||||
peer,
|
||||
data.vpsa_type().value_or_empty(),
|
||||
data.vpsa_message().value_or_empty());
|
||||
if (const auto history = _session->data().historyLoaded(peer)) {
|
||||
history->owner().histories().requestDialogEntry(history);
|
||||
}
|
||||
@@ -1865,7 +1865,7 @@ void ApiWrap::saveStickerSets(
|
||||
auto &sets = _session->data().stickerSetsRef();
|
||||
|
||||
_stickersOrder = localOrder;
|
||||
for_const (auto removedSetId, localRemoved) {
|
||||
for (const auto removedSetId : localRemoved) {
|
||||
if (removedSetId == Stickers::CloudRecentSetId) {
|
||||
if (sets.remove(Stickers::CloudRecentSetId) != 0) {
|
||||
writeCloudRecent = true;
|
||||
@@ -1890,16 +1890,17 @@ void ApiWrap::saveStickerSets(
|
||||
|
||||
auto it = sets.find(removedSetId);
|
||||
if (it != sets.cend()) {
|
||||
const auto set = it->second.get();
|
||||
for (auto i = recent.begin(); i != recent.cend();) {
|
||||
if (it->stickers.indexOf(i->first) >= 0) {
|
||||
if (set->stickers.indexOf(i->first) >= 0) {
|
||||
i = recent.erase(i);
|
||||
writeRecent = true;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if (!(it->flags & MTPDstickerSet::Flag::f_archived)) {
|
||||
MTPInputStickerSet setId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName));
|
||||
if (!(set->flags & MTPDstickerSet::Flag::f_archived)) {
|
||||
const auto setId = set->mtpInput();
|
||||
|
||||
auto requestId = request(MTPmessages_UninstallStickerSet(setId)).done([this](const MTPBool &result, mtpRequestId requestId) {
|
||||
stickerSetDisenabled(requestId);
|
||||
@@ -1909,60 +1910,69 @@ void ApiWrap::saveStickerSets(
|
||||
|
||||
_stickerSetDisenableRequests.insert(requestId);
|
||||
|
||||
int removeIndex = _session->data().stickerSetsOrder().indexOf(it->id);
|
||||
int removeIndex = _session->data().stickerSetsOrder().indexOf(set->id);
|
||||
if (removeIndex >= 0) _session->data().stickerSetsOrderRef().removeAt(removeIndex);
|
||||
if (!(it->flags & MTPDstickerSet_ClientFlag::f_featured)
|
||||
&& !(it->flags & MTPDstickerSet_ClientFlag::f_special)) {
|
||||
if (!(set->flags & MTPDstickerSet_ClientFlag::f_featured)
|
||||
&& !(set->flags & MTPDstickerSet_ClientFlag::f_special)) {
|
||||
sets.erase(it);
|
||||
} else {
|
||||
if (it->flags & MTPDstickerSet::Flag::f_archived) {
|
||||
if (set->flags & MTPDstickerSet::Flag::f_archived) {
|
||||
writeArchived = true;
|
||||
}
|
||||
it->flags &= ~(MTPDstickerSet::Flag::f_installed_date | MTPDstickerSet::Flag::f_archived);
|
||||
it->installDate = TimeId(0);
|
||||
set->flags &= ~(MTPDstickerSet::Flag::f_installed_date | MTPDstickerSet::Flag::f_archived);
|
||||
set->installDate = TimeId(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all installed flags, set only for sets from order.
|
||||
for (auto &set : sets) {
|
||||
if (!(set.flags & MTPDstickerSet::Flag::f_archived)) {
|
||||
set.flags &= ~MTPDstickerSet::Flag::f_installed_date;
|
||||
for (auto &[id, set] : sets) {
|
||||
if (!(set->flags & MTPDstickerSet::Flag::f_archived)) {
|
||||
set->flags &= ~MTPDstickerSet::Flag::f_installed_date;
|
||||
}
|
||||
}
|
||||
|
||||
auto &order = _session->data().stickerSetsOrderRef();
|
||||
order.clear();
|
||||
for_const (auto setId, _stickersOrder) {
|
||||
for (const auto setId : std::as_const(_stickersOrder)) {
|
||||
auto it = sets.find(setId);
|
||||
if (it != sets.cend()) {
|
||||
if ((it->flags & MTPDstickerSet::Flag::f_archived) && !localRemoved.contains(it->id)) {
|
||||
MTPInputStickerSet mtpSetId = (it->id && it->access) ? MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)) : MTP_inputStickerSetShortName(MTP_string(it->shortName));
|
||||
const auto set = it->second.get();
|
||||
if ((set->flags & MTPDstickerSet::Flag::f_archived) && !localRemoved.contains(set->id)) {
|
||||
const auto mtpSetId = set->mtpInput();
|
||||
|
||||
auto requestId = request(MTPmessages_InstallStickerSet(mtpSetId, MTP_boolFalse())).done([this](const MTPmessages_StickerSetInstallResult &result, mtpRequestId requestId) {
|
||||
const auto requestId = request(MTPmessages_InstallStickerSet(
|
||||
mtpSetId,
|
||||
MTP_boolFalse()
|
||||
)).done([=](
|
||||
const MTPmessages_StickerSetInstallResult &result,
|
||||
mtpRequestId requestId) {
|
||||
stickerSetDisenabled(requestId);
|
||||
}).fail([this](const RPCError &error, mtpRequestId requestId) {
|
||||
}).fail([=](
|
||||
const RPCError &error,
|
||||
mtpRequestId requestId) {
|
||||
stickerSetDisenabled(requestId);
|
||||
}).afterDelay(kSmallDelayMs).send();
|
||||
|
||||
_stickerSetDisenableRequests.insert(requestId);
|
||||
|
||||
it->flags &= ~MTPDstickerSet::Flag::f_archived;
|
||||
set->flags &= ~MTPDstickerSet::Flag::f_archived;
|
||||
writeArchived = true;
|
||||
}
|
||||
order.push_back(setId);
|
||||
it->flags |= MTPDstickerSet::Flag::f_installed_date;
|
||||
if (!it->installDate) {
|
||||
it->installDate = base::unixtime::now();
|
||||
set->flags |= MTPDstickerSet::Flag::f_installed_date;
|
||||
if (!set->installDate) {
|
||||
set->installDate = base::unixtime::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto it = sets.begin(); it != sets.cend();) {
|
||||
if ((it->flags & MTPDstickerSet_ClientFlag::f_featured)
|
||||
|| (it->flags & MTPDstickerSet::Flag::f_installed_date)
|
||||
|| (it->flags & MTPDstickerSet::Flag::f_archived)
|
||||
|| (it->flags & MTPDstickerSet_ClientFlag::f_special)) {
|
||||
const auto set = it->second.get();
|
||||
if ((set->flags & MTPDstickerSet_ClientFlag::f_featured)
|
||||
|| (set->flags & MTPDstickerSet::Flag::f_installed_date)
|
||||
|| (set->flags & MTPDstickerSet::Flag::f_archived)
|
||||
|| (set->flags & MTPDstickerSet_ClientFlag::f_special)) {
|
||||
++it;
|
||||
} else {
|
||||
it = sets.erase(it);
|
||||
@@ -3334,14 +3344,14 @@ void ApiWrap::readFeaturedSetDelayed(uint64 setId) {
|
||||
}
|
||||
|
||||
void ApiWrap::readFeaturedSets() {
|
||||
auto &sets = _session->data().stickerSetsRef();
|
||||
const auto &sets = _session->data().stickerSets();
|
||||
auto count = _session->data().featuredStickerSetsUnreadCount();
|
||||
QVector<MTPlong> wrappedIds;
|
||||
wrappedIds.reserve(_featuredSetsRead.size());
|
||||
for (auto setId : _featuredSetsRead) {
|
||||
auto it = sets.find(setId);
|
||||
for (const auto setId : _featuredSetsRead) {
|
||||
const auto it = sets.find(setId);
|
||||
if (it != sets.cend()) {
|
||||
it->flags &= ~MTPDstickerSet_ClientFlag::f_unread;
|
||||
it->second->flags &= ~MTPDstickerSet_ClientFlag::f_unread;
|
||||
wrappedIds.append(MTP_long(setId));
|
||||
if (count) {
|
||||
--count;
|
||||
@@ -4328,15 +4338,7 @@ void ApiWrap::forwardMessages(
|
||||
auto flags = MTPDmessage::Flags(0);
|
||||
auto clientFlags = MTPDmessage_ClientFlags();
|
||||
auto sendFlags = MTPmessages_ForwardMessages::Flags(0);
|
||||
if (channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
flags |= MTPDmessage::Flag::f_post;
|
||||
}
|
||||
if (!channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_from_id;
|
||||
} else if (peer->asChannel()->addsSignature()) {
|
||||
flags |= MTPDmessage::Flag::f_post_author;
|
||||
}
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_ForwardMessages::Flag::f_silent;
|
||||
}
|
||||
@@ -4489,15 +4491,7 @@ void ApiWrap::sendSharedContact(
|
||||
if (action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
if (channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
flags |= MTPDmessage::Flag::f_post;
|
||||
if (peer->asChannel()->addsSignature()) {
|
||||
flags |= MTPDmessage::Flag::f_post_author;
|
||||
}
|
||||
} else {
|
||||
flags |= MTPDmessage::Flag::f_from_id;
|
||||
}
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
} else {
|
||||
@@ -4874,15 +4868,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto silentPost = action.options.silent
|
||||
|| (channelPost && _session->data().notifySilentPosts(peer));
|
||||
if (channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
flags |= MTPDmessage::Flag::f_post;
|
||||
}
|
||||
if (!channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_from_id;
|
||||
} else if (peer->asChannel()->addsSignature()) {
|
||||
flags |= MTPDmessage::Flag::f_post_author;
|
||||
}
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_silent;
|
||||
}
|
||||
@@ -5019,15 +5005,7 @@ void ApiWrap::sendInlineResult(
|
||||
bool channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
bool silentPost = action.options.silent
|
||||
|| (channelPost && _session->data().notifySilentPosts(peer));
|
||||
if (channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
flags |= MTPDmessage::Flag::f_post;
|
||||
}
|
||||
if (!channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_from_id;
|
||||
} else if (peer->asChannel()->addsSignature()) {
|
||||
flags |= MTPDmessage::Flag::f_post_author;
|
||||
}
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_silent;
|
||||
}
|
||||
@@ -5754,16 +5732,6 @@ void ApiWrap::createPoll(
|
||||
if (action.options.scheduled) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||
}
|
||||
|
||||
const auto inputFlags = data.quiz()
|
||||
? MTPDinputMediaPoll::Flag::f_correct_answers
|
||||
: MTPDinputMediaPoll::Flag(0);
|
||||
auto correct = QVector<MTPbytes>();
|
||||
for (const auto &answer : data.answers) {
|
||||
if (answer.correct) {
|
||||
correct.push_back(MTP_bytes(answer.option));
|
||||
}
|
||||
}
|
||||
auto &histories = history->owner().histories();
|
||||
const auto requestType = Data::Histories::RequestType::Send;
|
||||
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||
@@ -5772,10 +5740,7 @@ void ApiWrap::createPoll(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
MTP_int(replyTo),
|
||||
MTP_inputMediaPoll(
|
||||
MTP_flags(inputFlags),
|
||||
PollDataToMTP(&data),
|
||||
MTP_vector<MTPbytes>(correct)),
|
||||
PollDataToInputMedia(&data),
|
||||
MTP_string(),
|
||||
MTP_long(rand_value<uint64>()),
|
||||
MTPReplyMarkup(),
|
||||
@@ -5852,25 +5817,12 @@ void ApiWrap::closePoll(not_null<HistoryItem*> item) {
|
||||
if (!poll) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto inputFlags = poll->quiz()
|
||||
? MTPDinputMediaPoll::Flag::f_correct_answers
|
||||
: MTPDinputMediaPoll::Flag(0);
|
||||
auto correct = QVector<MTPbytes>();
|
||||
for (const auto &answer : poll->answers) {
|
||||
if (answer.correct) {
|
||||
correct.push_back(MTP_bytes(answer.option));
|
||||
}
|
||||
}
|
||||
const auto requestId = request(MTPmessages_EditMessage(
|
||||
MTP_flags(MTPmessages_EditMessage::Flag::f_media),
|
||||
item->history()->peer->input,
|
||||
MTP_int(item->id),
|
||||
MTPstring(),
|
||||
MTP_inputMediaPoll(
|
||||
MTP_flags(inputFlags),
|
||||
PollDataToMTP(poll, true),
|
||||
MTP_vector<MTPbytes>(correct)),
|
||||
PollDataToInputMedia(poll, true),
|
||||
MTPReplyMarkup(),
|
||||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(0) // schedule_date
|
||||
@@ -5883,6 +5835,43 @@ void ApiWrap::closePoll(not_null<HistoryItem*> item) {
|
||||
_pollCloseRequestIds.emplace(itemId, requestId);
|
||||
}
|
||||
|
||||
void ApiWrap::rescheduleMessage(
|
||||
not_null<HistoryItem*> item,
|
||||
Api::SendOptions options) {
|
||||
const auto text = item->originalText().text;
|
||||
const auto sentEntities = Api::EntitiesToMTP(
|
||||
item->originalText().entities,
|
||||
Api::ConvertOption::SkipLocal);
|
||||
const auto media = item->media();
|
||||
|
||||
const auto emptyFlag = MTPmessages_EditMessage::Flag(0);
|
||||
const auto flags = MTPmessages_EditMessage::Flag::f_schedule_date
|
||||
| (!text.isEmpty()
|
||||
? MTPmessages_EditMessage::Flag::f_message
|
||||
: emptyFlag)
|
||||
| ((!media || !media->webpage())
|
||||
? MTPmessages_EditMessage::Flag::f_no_webpage
|
||||
: emptyFlag)
|
||||
| (!sentEntities.v.isEmpty()
|
||||
? MTPmessages_EditMessage::Flag::f_entities
|
||||
: emptyFlag);
|
||||
|
||||
const auto id = _session->data().scheduledMessages().lookupId(item);
|
||||
request(MTPmessages_EditMessage(
|
||||
MTP_flags(flags),
|
||||
item->history()->peer->input,
|
||||
MTP_int(id),
|
||||
MTP_string(text),
|
||||
MTPInputMedia(),
|
||||
MTPReplyMarkup(),
|
||||
sentEntities,
|
||||
MTP_int(options.scheduled)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
applyUpdates(result);
|
||||
}).fail([](const RPCError &error) {
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::reloadPollResults(not_null<HistoryItem*> item) {
|
||||
const auto itemId = item->fullId();
|
||||
if (!IsServerMsgId(item->id)
|
||||
|
||||
@@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/flat_map.h"
|
||||
#include "base/flat_set.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "chat_helpers/stickers.h"
|
||||
#include "chat_helpers/stickers_set.h"
|
||||
#include "data/data_messages.h"
|
||||
|
||||
class TaskQueue;
|
||||
@@ -208,7 +208,7 @@ public:
|
||||
void requestChangelog(
|
||||
const QString &sinceVersion,
|
||||
Fn<void(const MTPUpdates &result)> callback);
|
||||
void refreshProxyPromotion();
|
||||
void refreshTopPromotion();
|
||||
void requestDeepLinkInfo(
|
||||
const QString &path,
|
||||
Fn<void(const MTPDhelp_deepLinkInfo &result)> callback);
|
||||
@@ -476,6 +476,10 @@ public:
|
||||
void closePoll(not_null<HistoryItem*> item);
|
||||
void reloadPollResults(not_null<HistoryItem*> item);
|
||||
|
||||
void rescheduleMessage(
|
||||
not_null<HistoryItem*> item,
|
||||
Api::SendOptions options);
|
||||
|
||||
private:
|
||||
struct MessageDataRequest {
|
||||
using Callbacks = QList<RequestMessageDataCallback>;
|
||||
@@ -646,8 +650,8 @@ private:
|
||||
|
||||
//void readFeeds(); // #feed
|
||||
|
||||
void getProxyPromotionDelayed(TimeId now, TimeId next);
|
||||
void proxyPromotionDone(const MTPhelp_ProxyData &proxy);
|
||||
void getTopPromotionDelayed(TimeId now, TimeId next);
|
||||
void topPromotionDone(const MTPhelp_PromoData &proxy);
|
||||
|
||||
void sendNotifySettingsUpdates();
|
||||
|
||||
@@ -787,10 +791,10 @@ private:
|
||||
//base::flat_map<not_null<Data::Feed*>, mtpRequestId> _feedReadRequests;
|
||||
//base::Timer _feedReadTimer;
|
||||
|
||||
mtpRequestId _proxyPromotionRequestId = 0;
|
||||
std::pair<QString, uint32> _proxyPromotionKey;
|
||||
TimeId _proxyPromotionNextRequestTime = TimeId(0);
|
||||
base::Timer _proxyPromotionTimer;
|
||||
mtpRequestId _topPromotionRequestId = 0;
|
||||
std::pair<QString, uint32> _topPromotionKey;
|
||||
TimeId _topPromotionNextRequestTime = TimeId(0);
|
||||
base::Timer _topPromotionTimer;
|
||||
|
||||
base::flat_set<not_null<const PeerData*>> _updateNotifySettingsPeers;
|
||||
base::Timer _updateNotifySettingsTimer;
|
||||
|
||||
@@ -57,7 +57,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kImageAreaLimit = 6'016 * 3'384;
|
||||
constexpr auto kImageAreaLimit = 12'032 * 9'024;
|
||||
|
||||
App::LaunchState _launchState = App::Launched;
|
||||
|
||||
@@ -227,8 +227,6 @@ namespace App {
|
||||
clearCorners();
|
||||
|
||||
Data::clearGlobalStructures();
|
||||
|
||||
Images::ClearAll();
|
||||
}
|
||||
|
||||
void hoveredItem(HistoryView::Element *item) {
|
||||
@@ -322,6 +320,9 @@ namespace App {
|
||||
}
|
||||
|
||||
QImage readImage(QByteArray data, QByteArray *format, bool opaque, bool *animated) {
|
||||
if (data.isEmpty()) {
|
||||
return QImage();
|
||||
}
|
||||
QByteArray tmpFormat;
|
||||
QImage result;
|
||||
QBuffer buffer(&data);
|
||||
|
||||
@@ -11,23 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/rect_part.h"
|
||||
|
||||
enum class ImageRoundRadius;
|
||||
class MainWindow;
|
||||
class MainWidget;
|
||||
class HistoryItem;
|
||||
class History;
|
||||
|
||||
namespace HistoryView {
|
||||
class Element;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace Media {
|
||||
namespace Clip {
|
||||
class Reader;
|
||||
} // namespace Clip
|
||||
} // namespace Media
|
||||
|
||||
using HistoryItemsMap = base::flat_set<not_null<HistoryItem*>>;
|
||||
using GifItems = QHash<Media::Clip::Reader*, HistoryItem*>;
|
||||
|
||||
enum RoundCorners : int {
|
||||
SmallMaskCorners = 0x00, // for images
|
||||
LargeMaskCorners,
|
||||
|
||||
@@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "platform/platform_file_utilities.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "core/update_checker.h"
|
||||
@@ -23,7 +23,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -109,7 +108,7 @@ void AboutBox::showVersionHistory() {
|
||||
|
||||
Ui::show(Box<InformBox>("The link to the current private alpha version of Telegram Desktop was copied to the clipboard."));
|
||||
} else {
|
||||
QDesktopServices::openUrl(qsl("https://desktop.telegram.org/changelog"));
|
||||
File::OpenUrl(qsl("https://desktop.telegram.org/changelog"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_cloud_file.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "apiwrap.h"
|
||||
@@ -222,6 +223,7 @@ private:
|
||||
}
|
||||
|
||||
not_null<PeerData*> peer;
|
||||
mutable std::shared_ptr<Data::CloudImageView> userpic;
|
||||
Ui::Text::String name, status;
|
||||
};
|
||||
void paintChat(Painter &p, const ChatRow &row, bool selected) const;
|
||||
@@ -1438,7 +1440,7 @@ void RevokePublicLinkBox::resizeEvent(QResizeEvent *e) {
|
||||
|
||||
void RevokePublicLinkBox::Inner::paintChat(Painter &p, const ChatRow &row, bool selected) const {
|
||||
auto peer = row.peer;
|
||||
peer->paintUserpicLeft(p, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize);
|
||||
peer->paintUserpicLeft(p, row.userpic, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize);
|
||||
|
||||
p.setPen(st::contactsNameFg);
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mtproto/sender.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "boxes/background_preview_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "app.h"
|
||||
@@ -53,28 +55,33 @@ QImage TakeMiddleSample(QImage original, QSize size) {
|
||||
|
||||
} // namespace
|
||||
|
||||
class BackgroundBox::Inner : public Ui::RpWidget, private base::Subscriber {
|
||||
class BackgroundBox::Inner final
|
||||
: public Ui::RpWidget
|
||||
, private base::Subscriber {
|
||||
public:
|
||||
Inner(
|
||||
QWidget *parent,
|
||||
not_null<Main::Session*> session);
|
||||
~Inner();
|
||||
|
||||
rpl::producer<Data::WallPaper> chooseEvents() const;
|
||||
rpl::producer<Data::WallPaper> removeRequests() const;
|
||||
|
||||
void removePaper(const Data::WallPaper &data);
|
||||
|
||||
~Inner();
|
||||
|
||||
protected:
|
||||
private:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
|
||||
private:
|
||||
void visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) override;
|
||||
|
||||
struct Paper {
|
||||
Data::WallPaper data;
|
||||
mutable std::shared_ptr<Data::DocumentMedia> dataMedia;
|
||||
mutable QPixmap thumbnail;
|
||||
};
|
||||
struct Selected {
|
||||
@@ -252,11 +259,19 @@ void BackgroundBox::Inner::resizeToContentAndPreload() {
|
||||
const auto rows = (count / kBackgroundsInRow)
|
||||
+ (count % kBackgroundsInRow ? 1 : 0);
|
||||
|
||||
resize(st::boxWideWidth, rows * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
|
||||
resize(
|
||||
st::boxWideWidth,
|
||||
(rows * (st::backgroundSize.height() + st::backgroundPadding)
|
||||
+ st::backgroundPadding));
|
||||
|
||||
const auto preload = kBackgroundsInRow * 3;
|
||||
for (const auto &paper : _papers | ranges::view::take(preload)) {
|
||||
paper.data.loadThumbnail();
|
||||
if (!paper.data.localThumbnail() && !paper.dataMedia) {
|
||||
if (const auto document = paper.data.document()) {
|
||||
paper.dataMedia = document->createMediaView();
|
||||
paper.dataMedia->thumbnailWanted(paper.data.fileOrigin());
|
||||
}
|
||||
}
|
||||
}
|
||||
update();
|
||||
}
|
||||
@@ -292,15 +307,24 @@ void BackgroundBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
|
||||
void BackgroundBox::Inner::validatePaperThumbnail(
|
||||
const Paper &paper) const {
|
||||
Expects(paper.data.thumbnail() != nullptr);
|
||||
|
||||
const auto thumbnail = paper.data.thumbnail();
|
||||
if (!paper.thumbnail.isNull()) {
|
||||
return;
|
||||
} else if (!thumbnail->loaded()) {
|
||||
thumbnail->load(paper.data.fileOrigin());
|
||||
return;
|
||||
}
|
||||
const auto localThumbnail = paper.data.localThumbnail();
|
||||
if (!localThumbnail) {
|
||||
if (const auto document = paper.data.document()) {
|
||||
if (!paper.dataMedia) {
|
||||
paper.dataMedia = document->createMediaView();
|
||||
paper.dataMedia->thumbnailWanted(paper.data.fileOrigin());
|
||||
}
|
||||
}
|
||||
if (!paper.dataMedia || !paper.dataMedia->thumbnail()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto thumbnail = localThumbnail
|
||||
? localThumbnail
|
||||
: paper.dataMedia->thumbnail();
|
||||
auto original = thumbnail->original();
|
||||
if (paper.data.isPattern()) {
|
||||
const auto color = *paper.data.backgroundColor();
|
||||
@@ -428,7 +452,14 @@ void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (base::get_if<DeleteSelected>(&_over)) {
|
||||
_backgroundRemove.fire_copy(_papers[index].data);
|
||||
} else if (base::get_if<Selected>(&_over)) {
|
||||
_backgroundChosen.fire_copy(_papers[index].data);
|
||||
auto &paper = _papers[index];
|
||||
if (!paper.dataMedia) {
|
||||
if (const auto document = paper.data.document()) {
|
||||
// Keep it alive while it is on the screen.
|
||||
paper.dataMedia = document->createMediaView();
|
||||
}
|
||||
}
|
||||
_backgroundChosen.fire_copy(paper.data);
|
||||
}
|
||||
}
|
||||
} else if (!_over.has_value()) {
|
||||
@@ -436,6 +467,22 @@ void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::visibleTopBottomUpdated(
|
||||
int visibleTop,
|
||||
int visibleBottom) {
|
||||
for (auto i = 0, count = int(_papers.size()); i != count; ++i) {
|
||||
const auto row = (i / kBackgroundsInRow);
|
||||
const auto height = st::backgroundSize.height();
|
||||
const auto skip = st::backgroundPadding;
|
||||
const auto top = skip + row * (height + skip);
|
||||
const auto bottom = top + height;
|
||||
if ((bottom <= visibleTop || top >= visibleBottom)
|
||||
&& !_papers[i].thumbnail.isNull()) {
|
||||
_papers[i].dataMedia = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<Data::WallPaper> BackgroundBox::Inner::chooseEvents() const {
|
||||
return _backgroundChosen.events();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/background_preview_box.h"
|
||||
@@ -410,7 +412,11 @@ BackgroundPreviewBox::BackgroundPreviewBox(
|
||||
tr::lng_background_text2(tr::now),
|
||||
true))
|
||||
, _paper(paper)
|
||||
, _media(_paper.document() ? _paper.document()->createMediaView() : nullptr)
|
||||
, _radial([=](crl::time now) { radialAnimationCallback(now); }) {
|
||||
if (_media) {
|
||||
_media->thumbnailWanted(_paper.fileOrigin());
|
||||
}
|
||||
subscribe(_session->downloaderTaskFinished(), [=] { update(); });
|
||||
}
|
||||
|
||||
@@ -428,12 +434,14 @@ void BackgroundPreviewBox::prepare() {
|
||||
}
|
||||
updateServiceBg(_paper.backgroundColor());
|
||||
|
||||
_paper.loadThumbnail();
|
||||
_paper.loadDocument();
|
||||
if (_paper.document() && _paper.document()->loading()) {
|
||||
_radial.start(_paper.document()->progress());
|
||||
const auto document = _paper.document();
|
||||
if (document && document->loading()) {
|
||||
_radial.start(_media->progress());
|
||||
}
|
||||
if (_paper.thumbnail() && !_paper.isPattern()) {
|
||||
if (!_paper.isPattern()
|
||||
&& (_paper.localThumbnail()
|
||||
|| (document && document->hasThumbnail()))) {
|
||||
createBlurCheckbox();
|
||||
}
|
||||
setScaledFromThumb();
|
||||
@@ -634,7 +642,7 @@ void BackgroundPreviewBox::radialAnimationCallback(crl::time now) {
|
||||
const auto document = _paper.document();
|
||||
const auto wasAnimating = _radial.animating();
|
||||
const auto updated = _radial.update(
|
||||
document->progress(),
|
||||
_media->progress(),
|
||||
!document->loading(),
|
||||
now);
|
||||
if ((wasAnimating || _radial.animating())
|
||||
@@ -645,8 +653,13 @@ void BackgroundPreviewBox::radialAnimationCallback(crl::time now) {
|
||||
}
|
||||
|
||||
bool BackgroundPreviewBox::setScaledFromThumb() {
|
||||
const auto thumbnail = _paper.thumbnail();
|
||||
if (!thumbnail || !thumbnail->loaded()) {
|
||||
const auto localThumbnail = _paper.localThumbnail();
|
||||
const auto thumbnail = localThumbnail
|
||||
? localThumbnail
|
||||
: _media
|
||||
? _media->thumbnail()
|
||||
: nullptr;
|
||||
if (!thumbnail) {
|
||||
return false;
|
||||
} else if (_paper.isPattern() && _paper.document() != nullptr) {
|
||||
return false;
|
||||
@@ -710,7 +723,7 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
||||
const auto document = _paper.document();
|
||||
if (!_full.isNull()
|
||||
|| !document
|
||||
|| !document->loaded(DocumentData::FilePathResolve::Checked)
|
||||
|| !_media->loaded(true)
|
||||
|| _generating) {
|
||||
return;
|
||||
}
|
||||
@@ -744,7 +757,7 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
||||
});
|
||||
};
|
||||
_generating = Data::ReadImageAsync(
|
||||
document,
|
||||
_media.get(),
|
||||
Window::Theme::ProcessBackgroundImage,
|
||||
generateCallback);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
|
||||
namespace Data {
|
||||
class DocumentMedia;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@@ -71,6 +75,7 @@ private:
|
||||
AdminLog::OwnedItem _text1;
|
||||
AdminLog::OwnedItem _text2;
|
||||
Data::WallPaper _paper;
|
||||
std::shared_ptr<Data::DocumentMedia> _media;
|
||||
QImage _full;
|
||||
QPixmap _scaled, _blurred, _fadeOutThumbnail;
|
||||
Ui::Animations::Simple _fadeIn;
|
||||
|
||||
@@ -252,7 +252,7 @@ peerListBox: PeerList(defaultPeerList) {
|
||||
}
|
||||
|
||||
localStorageRowHeight: 50px;
|
||||
localStorageRowPadding: margins(23px, 5px, 20px, 5px);
|
||||
localStorageRowPadding: margins(22px, 5px, 20px, 5px);
|
||||
localStorageRowTitle: FlatLabel(defaultFlatLabel) {
|
||||
textFg: windowBoldFg;
|
||||
maxHeight: 20px;
|
||||
@@ -275,11 +275,11 @@ localStorageClear: defaultBoxButton;
|
||||
localStorageLimitLabel: LabelSimple(defaultLabelSimple) {
|
||||
font: boxTextFont;
|
||||
}
|
||||
localStorageLimitLabelMargin: margins(23px, 10px, 20px, 5px);
|
||||
localStorageLimitLabelMargin: margins(22px, 10px, 20px, 5px);
|
||||
localStorageLimitSlider: MediaSlider(defaultContinuousSlider) {
|
||||
seekSize: size(15px, 15px);
|
||||
}
|
||||
localStorageLimitMargin: margins(23px, 5px, 20px, 10px);
|
||||
localStorageLimitMargin: margins(22px, 5px, 20px, 10px);
|
||||
|
||||
shareRowsTop: 12px;
|
||||
shareRowHeight: 108px;
|
||||
@@ -340,7 +340,7 @@ sessionsHeight: 350px;
|
||||
sessionHeight: 70px;
|
||||
sessionCurrentPadding: margins(0px, 7px, 0px, 4px);
|
||||
sessionCurrentHeight: 118px;
|
||||
sessionPadding: margins(23px, 10px, 23px, 0px);
|
||||
sessionPadding: margins(22px, 10px, 22px, 0px);
|
||||
sessionNameFont: msgNameFont;
|
||||
sessionNameFg: boxTextFg;
|
||||
sessionWhenFont: msgDateFont;
|
||||
@@ -428,7 +428,7 @@ aboutLabel: FlatLabel(defaultFlatLabel) {
|
||||
}
|
||||
|
||||
autoDownloadTopDelta: 10px;
|
||||
autoDownloadTitlePosition: point(23px, 18px);
|
||||
autoDownloadTitlePosition: point(22px, 18px);
|
||||
autoDownloadTitleFont: font(15px semibold);
|
||||
autoDownloadLimitSlider: MediaSlider(defaultContinuousSlider) {
|
||||
seekSize: size(15px, 15px);
|
||||
@@ -562,7 +562,7 @@ passcodeTextStyle: TextStyle(defaultTextStyle) {
|
||||
lineHeight: 20px;
|
||||
}
|
||||
|
||||
usernamePadding: margins(23px, 6px, 21px, 12px);
|
||||
usernamePadding: margins(22px, 6px, 21px, 12px);
|
||||
usernameSkip: 49px;
|
||||
usernameTextStyle: TextStyle(boxTextStyle, passcodeTextStyle) {
|
||||
}
|
||||
@@ -642,9 +642,9 @@ rightsToggle: Toggle(defaultToggle) {
|
||||
}
|
||||
rightsDividerHeight: boxDividerHeight;
|
||||
rightsDividerMargin: margins(0px, 0px, 0px, 20px);
|
||||
rightsHeaderMargin: margins(23px, 0px, 23px, 8px);
|
||||
rightsToggleMargin: margins(23px, 8px, 23px, 8px);
|
||||
rightsAboutMargin: margins(23px, 8px, 23px, 8px);
|
||||
rightsHeaderMargin: margins(22px, 0px, 22px, 8px);
|
||||
rightsToggleMargin: margins(22px, 8px, 22px, 8px);
|
||||
rightsAboutMargin: margins(22px, 8px, 22px, 8px);
|
||||
rightsPhotoButton: UserpicButton(defaultUserpicButton) {
|
||||
size: size(60px, 60px);
|
||||
photoSize: 60px;
|
||||
@@ -773,8 +773,8 @@ markdownLinkFieldPadding: margins(22px, 0px, 22px, 10px);
|
||||
termsContent: FlatLabel(defaultFlatLabel) {
|
||||
minWidth: 285px;
|
||||
}
|
||||
termsPadding: margins(23px, 4px, 16px, 16px);
|
||||
termsAgePadding: margins(23px, 16px, 16px, 0px);
|
||||
termsPadding: margins(22px, 4px, 16px, 16px);
|
||||
termsAgePadding: margins(22px, 16px, 16px, 0px);
|
||||
|
||||
themesSmallSkip: 10px;
|
||||
themesBackgroundSize: 120px;
|
||||
@@ -800,7 +800,7 @@ themesMenuPosition: point(-2px, 25px);
|
||||
|
||||
createPollField: InputField(defaultInputField) {
|
||||
font: boxTextFont;
|
||||
textMargins: margins(0px, 0px, 0px, 0px);
|
||||
textMargins: margins(0px, 4px, 0px, 4px);
|
||||
textAlign: align(left);
|
||||
heightMin: 36px;
|
||||
heightMax: 86px;
|
||||
@@ -822,6 +822,11 @@ createPollOptionField: InputField(createPollField) {
|
||||
placeholderMargins: margins(2px, 0px, 2px, 0px);
|
||||
heightMax: 68px;
|
||||
}
|
||||
createPollSolutionField: InputField(createPollField) {
|
||||
textMargins: margins(0px, 4px, 0px, 4px);
|
||||
border: 1px;
|
||||
borderActive: 2px;
|
||||
}
|
||||
createPollLimitLabel: FlatLabel(defaultFlatLabel) {
|
||||
minWidth: 274px;
|
||||
align: align(topleft);
|
||||
@@ -855,7 +860,7 @@ createPollWarning: FlatLabel(defaultFlatLabel) {
|
||||
}
|
||||
}
|
||||
createPollWarningPosition: point(16px, 6px);
|
||||
createPollCheckboxMargin: margins(23px, 10px, 23px, 10px);
|
||||
createPollCheckboxMargin: margins(22px, 10px, 22px, 10px);
|
||||
createPollFieldTitlePadding: margins(22px, 7px, 10px, 6px);
|
||||
|
||||
callSettingsButton: IconButton {
|
||||
@@ -910,7 +915,7 @@ blockUserConfirmation: FlatLabel(boxLabel) {
|
||||
minWidth: 240px;
|
||||
}
|
||||
|
||||
transferCheckWidth: 300px;
|
||||
transferCheckWidth: 320px;
|
||||
|
||||
slowmodeLabelsMargin: margins(0px, 5px, 0px, 0px);
|
||||
slowmodeLabel: LabelSimple(defaultLabelSimple) {
|
||||
|
||||
@@ -599,4 +599,19 @@ void CalendarBox::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
void CalendarBox::wheelEvent(QWheelEvent *e) {
|
||||
// Only a mouse wheel is accepted.
|
||||
constexpr auto step = static_cast<int>(QWheelEvent::DefaultDeltasPerStep);
|
||||
const auto delta = e->angleDelta().y();
|
||||
if (std::abs(delta) != step) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (delta < 0) {
|
||||
goPreviousMonth();
|
||||
} else {
|
||||
goNextMonth();
|
||||
}
|
||||
}
|
||||
|
||||
CalendarBox::~CalendarBox() = default;
|
||||
|
||||
@@ -46,6 +46,7 @@ protected:
|
||||
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void wheelEvent(QWheelEvent *e) override;
|
||||
|
||||
private:
|
||||
void monthChanged(QDate month);
|
||||
|
||||
@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "mtproto/facade.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
@@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_photo_media.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "main/main_session.h"
|
||||
#include "observer_peer.h"
|
||||
@@ -894,12 +895,12 @@ ConfirmInviteBox::ConfirmInviteBox(
|
||||
|
||||
const auto photo = _session->data().processPhoto(data.vphoto());
|
||||
if (!photo->isNull()) {
|
||||
_photo = photo->thumbnail();
|
||||
if (!_photo->loaded()) {
|
||||
_photo = photo->createMediaView();
|
||||
_photo->wanted(Data::PhotoSize::Small, Data::FileOrigin());
|
||||
if (!_photo->image(Data::PhotoSize::Small)) {
|
||||
subscribe(_session->downloaderTaskFinished(), [=] {
|
||||
update();
|
||||
});
|
||||
_photo->load(Data::FileOrigin());
|
||||
}
|
||||
} else {
|
||||
_photoEmpty = std::make_unique<Ui::EmptyUserpic>(
|
||||
@@ -908,19 +909,20 @@ ConfirmInviteBox::ConfirmInviteBox(
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<not_null<UserData*>> ConfirmInviteBox::GetParticipants(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDchatInvite &data) {
|
||||
auto ConfirmInviteBox::GetParticipants(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDchatInvite &data)
|
||||
-> std::vector<Participant> {
|
||||
const auto participants = data.vparticipants();
|
||||
if (!participants) {
|
||||
return {};
|
||||
}
|
||||
const auto &v = participants->v;
|
||||
auto result = std::vector<not_null<UserData*>>();
|
||||
auto result = std::vector<Participant>();
|
||||
result.reserve(v.size());
|
||||
for (const auto &participant : v) {
|
||||
if (const auto user = session->data().processUser(participant)) {
|
||||
result.push_back(user);
|
||||
result.push_back(Participant{ user });
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -945,12 +947,12 @@ void ConfirmInviteBox::prepare() {
|
||||
_userWidth = (st::confirmInviteUserPhotoSize + 2 * padding);
|
||||
int sumWidth = _participants.size() * _userWidth;
|
||||
int left = (st::boxWideWidth - sumWidth) / 2;
|
||||
for (const auto user : _participants) {
|
||||
for (const auto &participant : _participants) {
|
||||
auto name = new Ui::FlatLabel(this, st::confirmInviteUserName);
|
||||
name->resizeToWidth(st::confirmInviteUserPhotoSize + padding);
|
||||
name->setText(user->firstName.isEmpty()
|
||||
? user->name
|
||||
: user->firstName);
|
||||
name->setText(participant.user->firstName.isEmpty()
|
||||
? participant.user->name
|
||||
: participant.user->firstName);
|
||||
name->moveToLeft(left + (padding / 2), st::confirmInviteUserNameTop);
|
||||
left += _userWidth;
|
||||
}
|
||||
@@ -972,14 +974,15 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
if (_photo) {
|
||||
p.drawPixmap(
|
||||
(width() - st::confirmInvitePhotoSize) / 2,
|
||||
st::confirmInvitePhotoTop,
|
||||
_photo->pixCircled(
|
||||
Data::FileOrigin(),
|
||||
st::confirmInvitePhotoSize,
|
||||
st::confirmInvitePhotoSize));
|
||||
} else {
|
||||
if (const auto image = _photo->image(Data::PhotoSize::Small)) {
|
||||
p.drawPixmap(
|
||||
(width() - st::confirmInvitePhotoSize) / 2,
|
||||
st::confirmInvitePhotoTop,
|
||||
image->pixCircled(
|
||||
st::confirmInvitePhotoSize,
|
||||
st::confirmInvitePhotoSize));
|
||||
}
|
||||
} else if (_photoEmpty) {
|
||||
_photoEmpty->paint(
|
||||
p,
|
||||
(width() - st::confirmInvitePhotoSize) / 2,
|
||||
@@ -990,9 +993,10 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) {
|
||||
|
||||
int sumWidth = _participants.size() * _userWidth;
|
||||
int left = (width() - sumWidth) / 2;
|
||||
for_const (auto user, _participants) {
|
||||
user->paintUserpicLeft(
|
||||
for (auto &participant : _participants) {
|
||||
participant.user->paintUserpicLeft(
|
||||
p,
|
||||
participant.userpic,
|
||||
left + (_userWidth - st::confirmInviteUserPhotoSize) / 2,
|
||||
st::confirmInviteUserPhotoTop,
|
||||
width(),
|
||||
|
||||
@@ -10,6 +10,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "mtproto/mtproto_rpc_sender.h"
|
||||
|
||||
namespace Data {
|
||||
class PhotoMedia;
|
||||
class CloudImageView;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@@ -221,7 +226,11 @@ protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
static std::vector<not_null<UserData*>> GetParticipants(
|
||||
struct Participant {
|
||||
not_null<UserData*> user;
|
||||
std::shared_ptr<Data::CloudImageView> userpic;
|
||||
};
|
||||
static std::vector<Participant> GetParticipants(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDchatInvite &data);
|
||||
|
||||
@@ -230,9 +239,9 @@ private:
|
||||
Fn<void()> _submit;
|
||||
object_ptr<Ui::FlatLabel> _title;
|
||||
object_ptr<Ui::FlatLabel> _status;
|
||||
Image *_photo = nullptr;
|
||||
std::shared_ptr<Data::PhotoMedia> _photo;
|
||||
std::unique_ptr<Ui::EmptyUserpic> _photoEmpty;
|
||||
std::vector<not_null<UserData*>> _participants;
|
||||
std::vector<Participant> _participants;
|
||||
bool _isChannel = false;
|
||||
|
||||
int _userWidth = 0;
|
||||
|
||||
@@ -38,6 +38,8 @@ constexpr auto kMaxOptionsCount = PollData::kMaxOptions;
|
||||
constexpr auto kOptionLimit = 100;
|
||||
constexpr auto kWarnQuestionLimit = 80;
|
||||
constexpr auto kWarnOptionLimit = 30;
|
||||
constexpr auto kSolutionLimit = 200;
|
||||
constexpr auto kWarnSolutionLimit = 60;
|
||||
constexpr auto kErrorLimit = 99;
|
||||
|
||||
class Options {
|
||||
@@ -59,6 +61,7 @@ public:
|
||||
[[nodiscard]] rpl::producer<int> usedCount() const;
|
||||
[[nodiscard]] rpl::producer<not_null<QWidget*>> scrollToWidget() const;
|
||||
[[nodiscard]] rpl::producer<> backspaceInFront() const;
|
||||
[[nodiscard]] rpl::producer<> tabbed() const;
|
||||
|
||||
private:
|
||||
class Option {
|
||||
@@ -80,7 +83,7 @@ private:
|
||||
void show(anim::type animated);
|
||||
void destroy(FnMut<void()> done);
|
||||
|
||||
[[nodisacrd]] bool hasShadow() const;
|
||||
[[nodiscard]] bool hasShadow() const;
|
||||
void createShadow();
|
||||
void destroyShadow();
|
||||
|
||||
@@ -146,6 +149,7 @@ private:
|
||||
bool _hasCorrect = false;
|
||||
rpl::event_stream<not_null<QWidget*>> _scrollToWidget;
|
||||
rpl::event_stream<> _backspaceInFront;
|
||||
rpl::event_stream<> _tabbed;
|
||||
|
||||
};
|
||||
|
||||
@@ -217,6 +221,7 @@ Options::Option::Option(
|
||||
InitField(outer, _field, session);
|
||||
_field->setMaxLength(kOptionLimit + kErrorLimit);
|
||||
_field->show();
|
||||
_field->customTab(true);
|
||||
|
||||
_wrap->hide(anim::type::instant);
|
||||
|
||||
@@ -497,6 +502,10 @@ rpl::producer<> Options::backspaceInFront() const {
|
||||
return _backspaceInFront.events();
|
||||
}
|
||||
|
||||
rpl::producer<> Options::tabbed() const {
|
||||
return _tabbed.events();
|
||||
}
|
||||
|
||||
void Options::Option::show(anim::type animated) {
|
||||
_wrap->show(animated);
|
||||
}
|
||||
@@ -647,6 +656,14 @@ void Options::addEmptyOption() {
|
||||
QObject::connect(field, &Ui::InputField::focused, [=] {
|
||||
_scrollToWidget.fire_copy(field);
|
||||
});
|
||||
QObject::connect(field, &Ui::InputField::tabbed, [=] {
|
||||
const auto index = findField(field);
|
||||
if (index + 1 < _list.size()) {
|
||||
_list[index + 1]->setFocus();
|
||||
} else {
|
||||
_tabbed.fire({});
|
||||
}
|
||||
});
|
||||
base::install_event_filter(field, [=](not_null<QEvent*> event) {
|
||||
if (event->type() != QEvent::KeyPress
|
||||
|| !field->getLastText().isEmpty()) {
|
||||
@@ -768,6 +785,7 @@ not_null<Ui::InputField*> CreatePollBox::setupQuestion(
|
||||
InitField(getDelegate()->outerContainer(), question, _session);
|
||||
question->setMaxLength(kQuestionLimit + kErrorLimit);
|
||||
question->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
|
||||
question->customTab(true);
|
||||
|
||||
const auto warning = CreateWarningLabel(
|
||||
container,
|
||||
@@ -794,6 +812,69 @@ not_null<Ui::InputField*> CreatePollBox::setupQuestion(
|
||||
return question;
|
||||
}
|
||||
|
||||
not_null<Ui::InputField*> CreatePollBox::setupSolution(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<bool> shown) {
|
||||
using namespace Settings;
|
||||
|
||||
const auto outer = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container))
|
||||
)->setDuration(0)->toggleOn(std::move(shown));
|
||||
const auto inner = outer->entity();
|
||||
|
||||
AddSkip(inner);
|
||||
AddSubsectionTitle(inner, tr::lng_polls_solution_title());
|
||||
const auto solution = inner->add(
|
||||
object_ptr<Ui::InputField>(
|
||||
inner,
|
||||
st::createPollSolutionField,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
tr::lng_polls_solution_placeholder()),
|
||||
st::createPollFieldPadding);
|
||||
InitField(getDelegate()->outerContainer(), solution, _session);
|
||||
solution->setMaxLength(kSolutionLimit + kErrorLimit);
|
||||
solution->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
solution->setInstantReplacesEnabled(
|
||||
_session->settings().replaceEmojiValue());
|
||||
solution->setMarkdownReplacesEnabled(rpl::single(true));
|
||||
solution->setEditLinkCallback(
|
||||
DefaultEditLinkCallback(_session, solution));
|
||||
solution->customTab(true);
|
||||
|
||||
const auto warning = CreateWarningLabel(
|
||||
inner,
|
||||
solution,
|
||||
kSolutionLimit,
|
||||
kWarnSolutionLimit);
|
||||
rpl::combine(
|
||||
solution->geometryValue(),
|
||||
warning->sizeValue()
|
||||
) | rpl::start_with_next([=](QRect geometry, QSize label) {
|
||||
warning->moveToLeft(
|
||||
(inner->width()
|
||||
- label.width()
|
||||
- st::createPollWarningPosition.x()),
|
||||
(geometry.y()
|
||||
- st::createPollFieldPadding.top()
|
||||
- st::settingsSubsectionTitlePadding.bottom()
|
||||
- st::settingsSubsectionTitle.style.font->height
|
||||
+ st::settingsSubsectionTitle.style.font->ascent
|
||||
- st::createPollWarning.style.font->ascent),
|
||||
geometry.width());
|
||||
}, warning->lifetime());
|
||||
|
||||
inner->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
inner,
|
||||
tr::lng_polls_solution_about(),
|
||||
st::boxDividerLabel),
|
||||
st::createPollFieldTitlePadding);
|
||||
|
||||
return solution;
|
||||
}
|
||||
|
||||
object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
using namespace Settings;
|
||||
|
||||
@@ -836,6 +917,10 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
st::boxDividerLabel),
|
||||
st::createPollLimitPadding));
|
||||
|
||||
connect(question, &Ui::InputField::tabbed, [=] {
|
||||
options->focusFirst();
|
||||
});
|
||||
|
||||
AddSkip(container);
|
||||
AddSubsectionTitle(container, tr::lng_polls_create_settings());
|
||||
|
||||
@@ -866,6 +951,24 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
(_chosen & PollData::Flag::Quiz),
|
||||
st::defaultCheckbox),
|
||||
st::createPollCheckboxMargin);
|
||||
|
||||
const auto solution = setupSolution(
|
||||
container,
|
||||
rpl::single(quiz->checked()) | rpl::then(quiz->checkedChanges()));
|
||||
|
||||
options->tabbed(
|
||||
) | rpl::start_with_next([=] {
|
||||
if (quiz->checked()) {
|
||||
solution->setFocus();
|
||||
} else {
|
||||
question->setFocus();
|
||||
}
|
||||
}, question->lifetime());
|
||||
|
||||
connect(solution, &Ui::InputField::tabbed, [=] {
|
||||
question->setFocus();
|
||||
});
|
||||
|
||||
quiz->setDisabled(_disabled & PollData::Flag::Quiz);
|
||||
if (multiple) {
|
||||
multiple->setDisabled((_disabled & PollData::Flag::MultiChoice)
|
||||
@@ -911,6 +1014,13 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
auto result = PollData(&_session->data(), id);
|
||||
result.question = question->getLastText().trimmed();
|
||||
result.answers = options->toPollAnswers();
|
||||
const auto solutionWithTags = quiz->checked()
|
||||
? solution->getTextWithAppliedMarkdown()
|
||||
: TextWithTags();
|
||||
result.solution = TextWithEntities{
|
||||
solutionWithTags.text,
|
||||
TextUtilities::ConvertTextTagsToEntities(solutionWithTags.tags)
|
||||
};
|
||||
const auto publicVotes = (anonymous && !anonymous->checked());
|
||||
const auto multiChoice = (multiple && multiple->checked());
|
||||
result.setFlags(Flag(0)
|
||||
@@ -937,6 +1047,12 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
} else {
|
||||
*error &= ~Error::Correct;
|
||||
}
|
||||
if (quiz->checked()
|
||||
&& solution->getLastText().trimmed().size() > kSolutionLimit) {
|
||||
*error |= Error::Solution;
|
||||
} else {
|
||||
*error &= ~Error::Solution;
|
||||
}
|
||||
};
|
||||
const auto showError = [=](const QString &text) {
|
||||
Ui::Toast::Show(text);
|
||||
@@ -951,6 +1067,8 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
options->focusFirst();
|
||||
} else if (*error & Error::Correct) {
|
||||
showError(tr::lng_polls_choose_correct(tr::now));
|
||||
} else if (*error & Error::Solution) {
|
||||
solution->showError();
|
||||
} else if (!*error) {
|
||||
_submitRequests.fire({ collectResult(), sendOptions });
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
PollData::Flags disabled,
|
||||
Api::SendType sendType);
|
||||
|
||||
rpl::producer<Result> submitRequests() const;
|
||||
[[nodiscard]] rpl::producer<Result> submitRequests() const;
|
||||
void submitFailed(const QString &error);
|
||||
|
||||
void setInnerFocus() override;
|
||||
@@ -50,13 +50,17 @@ private:
|
||||
Options = 0x02,
|
||||
Correct = 0x04,
|
||||
Other = 0x08,
|
||||
Solution = 0x10,
|
||||
};
|
||||
friend constexpr inline bool is_flag_type(Error) { return true; }
|
||||
using Errors = base::flags<Error>;
|
||||
|
||||
object_ptr<Ui::RpWidget> setupContent();
|
||||
not_null<Ui::InputField*> setupQuestion(
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> setupContent();
|
||||
[[nodiscard]] not_null<Ui::InputField*> setupQuestion(
|
||||
not_null<Ui::VerticalLayout*> container);
|
||||
[[nodiscard]] not_null<Ui::InputField*> setupSolution(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<bool> shown);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
const PollData::Flags _chosen = PollData::Flags();
|
||||
|
||||
@@ -22,12 +22,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_streaming.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_photo_media.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "layout.h"
|
||||
#include "media/clip/media_clip_reader.h"
|
||||
#include "media/streaming/media_streaming_instance.h"
|
||||
#include "media/streaming/media_streaming_player.h"
|
||||
#include "media/streaming/media_streaming_document.h"
|
||||
#include "media/streaming/media_streaming_loader_local.h"
|
||||
#include "storage/localimageloader.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
@@ -46,6 +53,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include <QtCore/QMimeData>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace ::Media::Streaming;
|
||||
using Data::PhotoSize;
|
||||
|
||||
} // namespace
|
||||
|
||||
EditCaptionBox::EditCaptionBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
@@ -60,32 +74,46 @@ EditCaptionBox::EditCaptionBox(
|
||||
|
||||
QSize dimensions;
|
||||
auto image = (Image*)nullptr;
|
||||
DocumentData *doc = nullptr;
|
||||
|
||||
const auto media = item->media();
|
||||
if (const auto photo = media->photo()) {
|
||||
_photoMedia = photo->createMediaView();
|
||||
_photoMedia->wanted(PhotoSize::Large, _msgId);
|
||||
image = _photoMedia->image(PhotoSize::Large);
|
||||
if (!image) {
|
||||
image = _photoMedia->image(PhotoSize::Thumbnail);
|
||||
if (!image) {
|
||||
image = _photoMedia->image(PhotoSize::Small);
|
||||
if (!image) {
|
||||
image = _photoMedia->thumbnailInline();
|
||||
}
|
||||
}
|
||||
}
|
||||
dimensions = _photoMedia->size(PhotoSize::Large);
|
||||
_photo = true;
|
||||
dimensions = QSize(photo->width(), photo->height());
|
||||
image = photo->large();
|
||||
} else if (const auto document = media->document()) {
|
||||
image = document->thumbnail();
|
||||
_documentMedia = document->createMediaView();
|
||||
_documentMedia->thumbnailWanted(_msgId);
|
||||
image = _documentMedia->thumbnail();
|
||||
dimensions = image
|
||||
? image->size()
|
||||
: document->dimensions;
|
||||
if (document->isAnimation()) {
|
||||
_gifw = document->dimensions.width();
|
||||
_gifh = document->dimensions.height();
|
||||
_gifw = style::ConvertScale(document->dimensions.width());
|
||||
_gifh = style::ConvertScale(document->dimensions.height());
|
||||
_animated = true;
|
||||
} else if (document->isVideoFile()) {
|
||||
_animated = true;
|
||||
} else {
|
||||
_doc = true;
|
||||
}
|
||||
doc = document;
|
||||
}
|
||||
const auto editData = PrepareEditText(item);
|
||||
|
||||
if (!_animated && (dimensions.isEmpty() || doc || !image)) {
|
||||
if (!_animated
|
||||
&& (dimensions.isEmpty()
|
||||
|| _documentMedia
|
||||
|| (!_photoMedia && !image))) {
|
||||
if (!image) {
|
||||
_thumbw = 0;
|
||||
} else {
|
||||
@@ -104,7 +132,7 @@ EditCaptionBox::EditCaptionBox(
|
||||
| Images::Option::RoundedBottomLeft
|
||||
| Images::Option::RoundedBottomRight;
|
||||
_thumb = App::pixmapFromImageInPlace(Images::prepare(
|
||||
image->pix(_msgId).toImage(),
|
||||
image->original(),
|
||||
_thumbw * cIntRetinaFactor(),
|
||||
0,
|
||||
options,
|
||||
@@ -113,19 +141,21 @@ EditCaptionBox::EditCaptionBox(
|
||||
};
|
||||
}
|
||||
|
||||
if (doc) {
|
||||
const auto nameString = doc->isVoiceMessage()
|
||||
if (_documentMedia) {
|
||||
const auto document = _documentMedia->owner();
|
||||
const auto nameString = document->isVoiceMessage()
|
||||
? tr::lng_media_audio(tr::now)
|
||||
: doc->composeNameString();
|
||||
setName(nameString, doc->size);
|
||||
_isImage = doc->isImage();
|
||||
_isAudio = (doc->isVoiceMessage() || doc->isAudioFile());
|
||||
: document->composeNameString();
|
||||
setName(nameString, document->size);
|
||||
_isImage = document->isImage();
|
||||
_isAudio = document->isVoiceMessage()
|
||||
|| document->isAudioFile();
|
||||
}
|
||||
if (_refreshThumbnail) {
|
||||
_refreshThumbnail();
|
||||
}
|
||||
} else {
|
||||
if (!image) {
|
||||
if (!image && !_photoMedia) {
|
||||
image = Image::BlankMedia();
|
||||
}
|
||||
auto maxW = 0, maxH = 0;
|
||||
@@ -150,24 +180,34 @@ EditCaptionBox::EditCaptionBox(
|
||||
const auto options = Images::Option::Smooth
|
||||
| Images::Option::Blurred;
|
||||
_thumb = image->pixNoCache(
|
||||
_msgId,
|
||||
maxW * cIntRetinaFactor(),
|
||||
maxH * cIntRetinaFactor(),
|
||||
options,
|
||||
maxW,
|
||||
maxH);
|
||||
};
|
||||
prepareGifPreview(doc);
|
||||
prepareStreamedPreview();
|
||||
} else {
|
||||
maxW = dimensions.width();
|
||||
maxH = dimensions.height();
|
||||
_thumbnailImage = image;
|
||||
_refreshThumbnail = [=] {
|
||||
_thumb = image->pixNoCache(
|
||||
_msgId,
|
||||
const auto photo = _photoMedia
|
||||
? _photoMedia->image(Data::PhotoSize::Large)
|
||||
: nullptr;
|
||||
const auto use = photo
|
||||
? photo
|
||||
: _thumbnailImage
|
||||
? _thumbnailImage
|
||||
: Image::BlankMedia().get();
|
||||
const auto options = Images::Option::Smooth
|
||||
| ((_photoMedia && !photo)
|
||||
? Images::Option::Blurred
|
||||
: Images::Option(0));
|
||||
_thumb = use->pixNoCache(
|
||||
maxW * cIntRetinaFactor(),
|
||||
maxH * cIntRetinaFactor(),
|
||||
Images::Option::Smooth,
|
||||
options,
|
||||
maxW,
|
||||
maxH);
|
||||
};
|
||||
@@ -205,7 +245,7 @@ EditCaptionBox::EditCaptionBox(
|
||||
thumbX = (st::boxWideWidth - thumbWidth) / 2;
|
||||
};
|
||||
|
||||
if (doc && doc->isAnimation()) {
|
||||
if (_documentMedia && _documentMedia->owner()->isAnimation()) {
|
||||
resizeDimensions(_gifw, _gifh, _gifx);
|
||||
}
|
||||
limitH = std::min(st::confirmMaxHeight, _gifh ? _gifh : INT_MAX);
|
||||
@@ -237,22 +277,36 @@ EditCaptionBox::EditCaptionBox(
|
||||
}
|
||||
Assert(_animated || _photo || _doc);
|
||||
|
||||
_thumbnailImageLoaded = _thumbnailImage
|
||||
? _thumbnailImage->loaded()
|
||||
_thumbnailImageLoaded = _photoMedia
|
||||
? (_photoMedia->image(Data::PhotoSize::Large) != nullptr)
|
||||
: _thumbnailImage
|
||||
? true
|
||||
: _documentMedia
|
||||
? !_documentMedia->owner()->hasThumbnail()
|
||||
: true;
|
||||
subscribe(_controller->session().downloaderTaskFinished(), [=] {
|
||||
if (!_thumbnailImageLoaded
|
||||
&& _thumbnailImage
|
||||
&& _thumbnailImage->loaded()) {
|
||||
_thumbnailImageLoaded = true;
|
||||
_refreshThumbnail();
|
||||
update();
|
||||
}
|
||||
if (doc && doc->isAnimation() && doc->loaded() && !_gifPreview) {
|
||||
prepareGifPreview(doc);
|
||||
}
|
||||
});
|
||||
|
||||
if (!_thumbnailImageLoaded) {
|
||||
subscribe(_controller->session().downloaderTaskFinished(), [=] {
|
||||
if (_thumbnailImageLoaded) {
|
||||
return;
|
||||
} else if (!_thumbnailImage
|
||||
&& _photoMedia
|
||||
&& _photoMedia->image(PhotoSize::Large)) {
|
||||
_thumbnailImage = _photoMedia->image(PhotoSize::Large);
|
||||
} else if (!_thumbnailImage
|
||||
&& _documentMedia
|
||||
&& _documentMedia->owner()->hasThumbnail()) {
|
||||
_thumbnailImage = _documentMedia->thumbnail();
|
||||
}
|
||||
if (_thumbnailImage) {
|
||||
_thumbnailImageLoaded = !_photoMedia
|
||||
|| _photoMedia->image(PhotoSize::Large);
|
||||
if (_thumbnailImageLoaded) {
|
||||
_refreshThumbnail();
|
||||
update();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
_field.create(
|
||||
this,
|
||||
st::confirmCaptionArea,
|
||||
@@ -287,6 +341,8 @@ EditCaptionBox::EditCaptionBox(
|
||||
}, _wayWrap->lifetime());
|
||||
}
|
||||
|
||||
EditCaptionBox::~EditCaptionBox() = default;
|
||||
|
||||
void EditCaptionBox::emojiFilterForGeometry(not_null<QEvent*> event) {
|
||||
const auto type = event->type();
|
||||
if (type == QEvent::Move || type == QEvent::Resize) {
|
||||
@@ -305,74 +361,86 @@ void EditCaptionBox::updateEmojiPanelGeometry() {
|
||||
local.x() + _emojiToggle->width() * 3);
|
||||
}
|
||||
|
||||
void EditCaptionBox::prepareGifPreview(DocumentData* document) {
|
||||
void EditCaptionBox::prepareStreamedPreview() {
|
||||
const auto isListEmpty = _preparedList.files.empty();
|
||||
if (_gifPreview) {
|
||||
if (_streamed) {
|
||||
return;
|
||||
} else if (!document && isListEmpty) {
|
||||
} else if (!_documentMedia && isListEmpty) {
|
||||
return;
|
||||
}
|
||||
const auto callback = [=](Media::Clip::Notification notification) {
|
||||
clipCallback(notification);
|
||||
};
|
||||
if (document && document->isAnimation() && document->loaded()) {
|
||||
_gifPreview = Media::Clip::MakeReader(
|
||||
document,
|
||||
_msgId,
|
||||
callback);
|
||||
const auto document = _documentMedia
|
||||
? _documentMedia->owner().get()
|
||||
: nullptr;
|
||||
if (document && document->isAnimation()) {
|
||||
setupStreamedPreview(
|
||||
document->owner().streaming().sharedDocument(
|
||||
document,
|
||||
_msgId));
|
||||
} else if (!isListEmpty) {
|
||||
const auto file = &_preparedList.files.front();
|
||||
if (file->path.isEmpty()) {
|
||||
_gifPreview = Media::Clip::MakeReader(
|
||||
file->content,
|
||||
callback);
|
||||
} else {
|
||||
_gifPreview = Media::Clip::MakeReader(
|
||||
file->path,
|
||||
callback);
|
||||
}
|
||||
auto loader = file->path.isEmpty()
|
||||
? MakeBytesLoader(file->content)
|
||||
: MakeFileLoader(file->path);
|
||||
setupStreamedPreview(std::make_shared<Document>(std::move(loader)));
|
||||
}
|
||||
if (_gifPreview) _gifPreview->setAutoplay();
|
||||
}
|
||||
|
||||
void EditCaptionBox::clipCallback(Media::Clip::Notification notification) {
|
||||
using namespace Media::Clip;
|
||||
switch (notification) {
|
||||
case NotificationReinit: {
|
||||
if (_gifPreview && _gifPreview->state() == State::Error) {
|
||||
_gifPreview.setBad();
|
||||
}
|
||||
void EditCaptionBox::setupStreamedPreview(std::shared_ptr<Document> shared) {
|
||||
if (!shared) {
|
||||
return;
|
||||
}
|
||||
_streamed = std::make_unique<Instance>(
|
||||
std::move(shared),
|
||||
[=] { update(); });
|
||||
_streamed->lockPlayer();
|
||||
_streamed->player().updates(
|
||||
) | rpl::start_with_next_error([=](Update &&update) {
|
||||
handleStreamingUpdate(std::move(update));
|
||||
}, [=](Error &&error) {
|
||||
handleStreamingError(std::move(error));
|
||||
}, _streamed->lifetime());
|
||||
|
||||
if (_gifPreview && _gifPreview->ready() && !_gifPreview->started()) {
|
||||
const auto calculateGifDimensions = [&]() {
|
||||
const auto scaled = QSize(
|
||||
_gifPreview->width(),
|
||||
_gifPreview->height()).scaled(
|
||||
st::sendMediaPreviewSize * cIntRetinaFactor(),
|
||||
st::confirmMaxHeight * cIntRetinaFactor(),
|
||||
Qt::KeepAspectRatio);
|
||||
_thumbw = _gifw = scaled.width();
|
||||
_thumbh = _gifh = scaled.height();
|
||||
_thumbx = _gifx = (st::boxWideWidth - _gifw) / 2;
|
||||
updateBoxSize();
|
||||
};
|
||||
// If gif file is not mp4,
|
||||
// Its dimension values will be known only after reading.
|
||||
if (_gifw <= 0 || _gifh <= 0) {
|
||||
calculateGifDimensions();
|
||||
}
|
||||
const auto s = QSize(_gifw, _gifh);
|
||||
_gifPreview->start(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None);
|
||||
}
|
||||
if (_streamed->ready()) {
|
||||
streamingReady(base::duplicate(_streamed->info()));
|
||||
}
|
||||
checkStreamedIsStarted();
|
||||
}
|
||||
|
||||
update();
|
||||
} break;
|
||||
void EditCaptionBox::handleStreamingUpdate(Update &&update) {
|
||||
update.data.match([&](Information &update) {
|
||||
streamingReady(std::move(update));
|
||||
}, [&](const PreloadedVideo &update) {
|
||||
}, [&](const UpdateVideo &update) {
|
||||
this->update();
|
||||
}, [&](const PreloadedAudio &update) {
|
||||
}, [&](const UpdateAudio &update) {
|
||||
}, [&](const WaitingForData &update) {
|
||||
}, [&](MutedByOther) {
|
||||
}, [&](Finished) {
|
||||
});
|
||||
}
|
||||
|
||||
case NotificationRepaint: {
|
||||
if (_gifPreview && !_gifPreview->currentDisplayed()) {
|
||||
update();
|
||||
}
|
||||
} break;
|
||||
void EditCaptionBox::handleStreamingError(Error &&error) {
|
||||
}
|
||||
|
||||
void EditCaptionBox::streamingReady(Information &&info) {
|
||||
const auto calculateGifDimensions = [&]() {
|
||||
const auto scaled = QSize(
|
||||
info.video.size.width(),
|
||||
info.video.size.height()
|
||||
).scaled(
|
||||
st::sendMediaPreviewSize * cIntRetinaFactor(),
|
||||
st::confirmMaxHeight * cIntRetinaFactor(),
|
||||
Qt::KeepAspectRatio);
|
||||
_thumbw = _gifw = scaled.width();
|
||||
_thumbh = _gifh = scaled.height();
|
||||
_thumbx = _gifx = (st::boxWideWidth - _gifw) / 2;
|
||||
updateBoxSize();
|
||||
};
|
||||
// If gif file is not mp4,
|
||||
// Its dimension values will be known only after reading.
|
||||
if (_gifw <= 0 || _gifh <= 0) {
|
||||
calculateGifDimensions();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,7 +458,7 @@ void EditCaptionBox::updateEditPreview() {
|
||||
_animated = false;
|
||||
_photo = false;
|
||||
_doc = false;
|
||||
_gifPreview = nullptr;
|
||||
_streamed = nullptr;
|
||||
_thumbw = _thumbh = _thumbx = 0;
|
||||
_gifw = _gifh = _gifx = 0;
|
||||
|
||||
@@ -469,7 +537,7 @@ void EditCaptionBox::updateEditPreview() {
|
||||
_gifw = _thumbw;
|
||||
_gifh = _thumbh;
|
||||
_gifx = _thumbx;
|
||||
prepareGifPreview();
|
||||
prepareStreamedPreview();
|
||||
}
|
||||
}
|
||||
updateEditMediaButton();
|
||||
@@ -548,18 +616,10 @@ void EditCaptionBox::prepare() {
|
||||
if (action == Ui::InputField::MimeAction::Check) {
|
||||
if (!data->hasText() && !_isAllowedEditMedia) {
|
||||
return false;
|
||||
}
|
||||
if (data->hasImage()) {
|
||||
const auto image = qvariant_cast<QImage>(data->imageData());
|
||||
if (!image.isNull()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (const auto urls = data->urls(); !urls.empty()) {
|
||||
if (ranges::find_if(
|
||||
urls,
|
||||
[](const QUrl &url) { return !url.isLocalFile(); }
|
||||
) == urls.end()) {
|
||||
} else if (data->hasImage()) {
|
||||
return true;
|
||||
} else if (const auto urls = data->urls(); !urls.empty()) {
|
||||
if (ranges::all_of(urls, &QUrl::isLocalFile)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -692,6 +752,31 @@ int EditCaptionBox::errorTopSkip() const {
|
||||
return (st::defaultBox.buttonPadding.top() / 2);
|
||||
}
|
||||
|
||||
void EditCaptionBox::checkStreamedIsStarted() {
|
||||
if (!_streamed) {
|
||||
return;
|
||||
}
|
||||
if (_streamed->paused()) {
|
||||
_streamed->resume();
|
||||
}
|
||||
if (!_streamed->active() && !_streamed->failed()) {
|
||||
startStreamedPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
void EditCaptionBox::startStreamedPlayer() {
|
||||
auto options = ::Media::Streaming::PlaybackOptions();
|
||||
options.audioId = _documentMedia
|
||||
? AudioMsgId(_documentMedia->owner(), _msgId)
|
||||
: AudioMsgId();
|
||||
options.waitForMarkAsShown = true;
|
||||
//if (!_streamed->withSound) {
|
||||
options.mode = ::Media::Streaming::Mode::Video;
|
||||
options.loop = true;
|
||||
//}
|
||||
_streamed->play(options);
|
||||
}
|
||||
|
||||
void EditCaptionBox::paintEvent(QPaintEvent *e) {
|
||||
BoxContent::paintEvent(e);
|
||||
|
||||
@@ -705,16 +790,27 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
|
||||
if (_thumbx + _thumbw < width() - st::boxPhotoPadding.right()) {
|
||||
p.fillRect(_thumbx + _thumbw, st::boxPhotoPadding.top(), width() - st::boxPhotoPadding.right() - _thumbx - _thumbw, th, st::confirmBg);
|
||||
}
|
||||
if (_gifPreview && _gifPreview->started()) {
|
||||
checkStreamedIsStarted();
|
||||
if (_streamed
|
||||
&& _streamed->player().ready()
|
||||
&& !_streamed->player().videoSize().isEmpty()) {
|
||||
const auto s = QSize(_gifw, _gifh);
|
||||
const auto paused = _controller->isGifPausedAtLeastFor(Window::GifPauseReason::Layer);
|
||||
const auto frame = _gifPreview->current(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None, paused ? 0 : crl::now());
|
||||
p.drawPixmap(_gifx, st::boxPhotoPadding.top(), frame);
|
||||
|
||||
auto request = ::Media::Streaming::FrameRequest();
|
||||
request.outer = s * cIntRetinaFactor();
|
||||
request.resize = s * cIntRetinaFactor();
|
||||
p.drawImage(
|
||||
QRect(_gifx, st::boxPhotoPadding.top(), _gifw, _gifh),
|
||||
_streamed->frame(request));
|
||||
if (!paused) {
|
||||
_streamed->markFrameShown();
|
||||
}
|
||||
} else {
|
||||
const auto offset = _gifh ? ((_gifh - _thumbh) / 2) : 0;
|
||||
p.drawPixmap(_thumbx, st::boxPhotoPadding.top() + offset, _thumb);
|
||||
}
|
||||
if (_animated && !_gifPreview) {
|
||||
if (_animated && !_streamed) {
|
||||
QRect inner(_thumbx + (_thumbw - st::msgFileSize) / 2, st::boxPhotoPadding.top() + (th - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::msgDateImgBg);
|
||||
|
||||
@@ -10,9 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "media/clip/media_clip_reader.h"
|
||||
#include "mtproto/mtproto_rpc_sender.h"
|
||||
|
||||
class Image;
|
||||
|
||||
namespace ChatHelpers {
|
||||
class TabbedPanel;
|
||||
} // namespace ChatHelpers
|
||||
@@ -23,6 +24,8 @@ class SessionController;
|
||||
|
||||
namespace Data {
|
||||
class Media;
|
||||
class PhotoMedia;
|
||||
class DocumentMedia;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
@@ -36,6 +39,16 @@ namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace Media {
|
||||
namespace Streaming {
|
||||
class Instance;
|
||||
class Document;
|
||||
struct Update;
|
||||
enum class Error;
|
||||
struct Information;
|
||||
} // namespace Streaming
|
||||
} // namespace Media
|
||||
|
||||
class EditCaptionBox
|
||||
: public Ui::BoxContent
|
||||
, public RPCSender
|
||||
@@ -45,6 +58,7 @@ public:
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<HistoryItem*> item);
|
||||
~EditCaptionBox();
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -56,8 +70,14 @@ protected:
|
||||
|
||||
private:
|
||||
void updateBoxSize();
|
||||
void prepareGifPreview(DocumentData* document = nullptr);
|
||||
void clipCallback(Media::Clip::Notification notification);
|
||||
void prepareStreamedPreview();
|
||||
void checkStreamedIsStarted();
|
||||
void setupStreamedPreview(
|
||||
std::shared_ptr<::Media::Streaming::Document> shared);
|
||||
void handleStreamingUpdate(::Media::Streaming::Update &&update);
|
||||
void handleStreamingError(::Media::Streaming::Error &&error);
|
||||
void streamingReady(::Media::Streaming::Information &&info);
|
||||
void startStreamedPlayer();
|
||||
|
||||
void setupEmojiPanel();
|
||||
void updateEmojiPanelGeometry();
|
||||
@@ -86,6 +106,8 @@ private:
|
||||
|
||||
not_null<Window::SessionController*> _controller;
|
||||
FullMsgId _msgId;
|
||||
std::shared_ptr<Data::PhotoMedia> _photoMedia;
|
||||
std::shared_ptr<Data::DocumentMedia> _documentMedia;
|
||||
Image *_thumbnailImage = nullptr;
|
||||
bool _thumbnailImageLoaded = false;
|
||||
Fn<void()> _refreshThumbnail;
|
||||
@@ -94,7 +116,7 @@ private:
|
||||
bool _doc = false;
|
||||
|
||||
QPixmap _thumb;
|
||||
Media::Clip::ReaderPointer _gifPreview;
|
||||
std::unique_ptr<::Media::Streaming::Instance> _streamed;
|
||||
|
||||
object_ptr<Ui::InputField> _field = { nullptr };
|
||||
object_ptr<Ui::EmojiButton> _emojiToggle = { nullptr };
|
||||
|
||||
@@ -78,6 +78,7 @@ private:
|
||||
};
|
||||
struct PeerButton {
|
||||
not_null<History*> history;
|
||||
std::shared_ptr<Data::CloudImageView> userpic;
|
||||
Button button;
|
||||
};
|
||||
|
||||
@@ -184,9 +185,10 @@ void FilterChatsPreview::updateData(
|
||||
}
|
||||
}
|
||||
for (const auto history : peers) {
|
||||
_removePeer.push_back({
|
||||
history,
|
||||
makeButton([=] { removePeer(history); }) });
|
||||
_removePeer.push_back(PeerButton{
|
||||
.history = history,
|
||||
.button = makeButton([=] { removePeer(history); })
|
||||
});
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
@@ -203,7 +205,7 @@ int FilterChatsPreview::resizeGetHeight(int newWidth) {
|
||||
for (const auto &[flag, button] : _removeFlag) {
|
||||
moveNextButton(button.get());
|
||||
}
|
||||
for (const auto &[history, button] : _removePeer) {
|
||||
for (const auto &[history, userpic, button] : _removePeer) {
|
||||
moveNextButton(button.get());
|
||||
}
|
||||
return top;
|
||||
@@ -235,7 +237,7 @@ void FilterChatsPreview::paintEvent(QPaintEvent *e) {
|
||||
FilterChatsTypeName(flag));
|
||||
top += st.height;
|
||||
}
|
||||
for (const auto &[history, button] : _removePeer) {
|
||||
for (auto &[history, userpic, button] : _removePeer) {
|
||||
const auto savedMessages = history->peer->isSelf();
|
||||
if (savedMessages) {
|
||||
Ui::EmptyUserpic::PaintSavedMessages(
|
||||
@@ -253,6 +255,7 @@ void FilterChatsPreview::paintEvent(QPaintEvent *e) {
|
||||
} else {
|
||||
history->peer->paintUserpicLeft(
|
||||
p,
|
||||
userpic,
|
||||
iconLeft,
|
||||
top + iconTop,
|
||||
width(),
|
||||
|
||||
@@ -180,11 +180,12 @@ QString ExceptionRow::generateShortName() {
|
||||
PaintRoundImageCallback ExceptionRow::generatePaintUserpicCallback() {
|
||||
const auto peer = this->peer();
|
||||
const auto saved = peer->isSelf();
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
auto userpic = saved ? nullptr : ensureUserpicView();
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
|
||||
if (saved) {
|
||||
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
|
||||
} else {
|
||||
peer->paintUserpicLeft(p, x, y, outerWidth, size);
|
||||
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -41,8 +41,9 @@ PaintRoundImageCallback PaintUserpicCallback(
|
||||
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
|
||||
};
|
||||
}
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
peer->paintUserpicLeft(p, x, y, outerWidth, size);
|
||||
auto userpic = std::shared_ptr<Data::CloudImageView>();
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
|
||||
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -483,14 +484,22 @@ QString PeerListRow::generateShortName() {
|
||||
: peer()->shortName();
|
||||
}
|
||||
|
||||
std::shared_ptr<Data::CloudImageView> PeerListRow::ensureUserpicView() {
|
||||
if (!_userpic) {
|
||||
_userpic = peer()->createUserpicView();
|
||||
}
|
||||
return _userpic;
|
||||
}
|
||||
|
||||
PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback() {
|
||||
const auto saved = _isSavedMessagesChat;
|
||||
const auto peer = this->peer();
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
auto userpic = saved ? nullptr : ensureUserpicView();
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
|
||||
if (saved) {
|
||||
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
|
||||
} else {
|
||||
peer->paintUserpicLeft(p, x, y, outerWidth, size);
|
||||
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -595,7 +604,7 @@ void PeerListRow::paintDisabledCheckUserpic(
|
||||
if (_isSavedMessagesChat) {
|
||||
Ui::EmptyUserpic::PaintSavedMessages(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
|
||||
} else {
|
||||
peer()->paintUserpicLeft(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
|
||||
peer()->paintUserpicLeft(p, _userpic, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -7,11 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/event_stream.h>
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/empty_userpic.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "data/data_cloud_file.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
namespace style {
|
||||
@@ -85,6 +85,8 @@ public:
|
||||
return _id;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Data::CloudImageView> ensureUserpicView();
|
||||
|
||||
[[nodiscard]] virtual QString generateName();
|
||||
[[nodiscard]] virtual QString generateShortName();
|
||||
[[nodiscard]] virtual auto generatePaintUserpicCallback()
|
||||
@@ -130,7 +132,7 @@ public:
|
||||
LastSeen,
|
||||
Custom,
|
||||
};
|
||||
void refreshStatus();
|
||||
virtual void refreshStatus();
|
||||
crl::time refreshStatusTime() const;
|
||||
|
||||
void setAbsoluteIndex(int index) {
|
||||
@@ -223,6 +225,7 @@ private:
|
||||
|
||||
PeerListRowId _id = 0;
|
||||
PeerData *_peer = nullptr;
|
||||
mutable std::shared_ptr<Data::CloudImageView> _userpic;
|
||||
std::unique_ptr<Ui::RippleAnimation> _ripple;
|
||||
std::unique_ptr<Ui::RoundImageCheckbox> _checkbox;
|
||||
Ui::Text::String _name;
|
||||
|
||||
@@ -584,5 +584,9 @@ void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
|
||||
auto ChooseRecipientBoxController::createRow(
|
||||
not_null<History*> history) -> std::unique_ptr<Row> {
|
||||
return std::make_unique<Row>(history);
|
||||
const auto peer = history->peer;
|
||||
const auto skip = peer->isChannel()
|
||||
&& !peer->isMegagroup()
|
||||
&& !peer->canWrite();
|
||||
return skip ? nullptr : std::make_unique<Row>(history);
|
||||
}
|
||||
|
||||
@@ -354,6 +354,16 @@ bool ParticipantsAdditionalData::canRestrictUser(
|
||||
Unexpected("Peer in ParticipantsAdditionalData::canRestrictUser.");
|
||||
}
|
||||
|
||||
bool ParticipantsAdditionalData::canRemoveUser(
|
||||
not_null<UserData*> user) const {
|
||||
if (canRestrictUser(user)) {
|
||||
return true;
|
||||
} else if (const auto chat = _peer->asChat()) {
|
||||
return chat->invitedByMe.contains(user);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ParticipantsAdditionalData::adminRights(
|
||||
not_null<UserData*> user) const
|
||||
-> std::optional<MTPChatAdminRights> {
|
||||
@@ -1056,7 +1066,9 @@ void ParticipantsBoxController::prepare() {
|
||||
switch (_role) {
|
||||
case Role::Admins: return tr::lng_channel_admins();
|
||||
case Role::Profile:
|
||||
case Role::Members: return tr::lng_profile_participants_section();
|
||||
case Role::Members: return (_peer->isChannel() && !_peer->isMegagroup()
|
||||
? tr::lng_profile_subscribers_section()
|
||||
: tr::lng_profile_participants_section());
|
||||
case Role::Restricted: return tr::lng_exceptions_list_title();
|
||||
case Role::Kicked: return tr::lng_removed_list_title();
|
||||
}
|
||||
@@ -1434,6 +1446,8 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
|
||||
tr::lng_context_restrict_user(tr::now),
|
||||
crl::guard(this, [=] { showRestricted(user); }));
|
||||
}
|
||||
}
|
||||
if (_additional.canRemoveUser(user)) {
|
||||
if (!_additional.isKicked(user)) {
|
||||
const auto isGroup = _peer->isChat() || _peer->isMegagroup();
|
||||
result->addAction(
|
||||
@@ -1786,6 +1800,12 @@ std::unique_ptr<PeerListRow> ParticipantsBoxController::createRow(
|
||||
|| _additional.canEditAdmin(user))) {
|
||||
row->setActionLink(tr::lng_profile_kick(tr::now));
|
||||
}
|
||||
if (_role == Role::Members && user->isBot()) {
|
||||
auto seesAllMessages = (user->botInfo->readsAllHistory || _additional.adminRights(user).has_value());
|
||||
row->setCustomStatus(seesAllMessages
|
||||
? tr::lng_status_bot_reads_all(tr::now)
|
||||
: tr::lng_status_bot_not_reads_all(tr::now));
|
||||
}
|
||||
}
|
||||
return row;
|
||||
}
|
||||
@@ -1798,7 +1818,7 @@ auto ParticipantsBoxController::computeType(
|
||||
: _additional.adminRights(user).has_value()
|
||||
? Rights::Admin
|
||||
: Rights::Normal;
|
||||
result.canRemove = _additional.canRestrictUser(user);
|
||||
result.canRemove = _additional.canRemoveUser(user);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ public:
|
||||
[[nodiscard]] bool canEditAdmin(not_null<UserData*> user) const;
|
||||
[[nodiscard]] bool canAddOrEditAdmin(not_null<UserData*> user) const;
|
||||
[[nodiscard]] bool canRestrictUser(not_null<UserData*> user) const;
|
||||
[[nodiscard]] bool canRemoveUser(not_null<UserData*> user) const;
|
||||
[[nodiscard]] std::optional<MTPChatAdminRights> adminRights(
|
||||
not_null<UserData*> user) const;
|
||||
QString adminRank(not_null<UserData*> user) const;
|
||||
|
||||
@@ -967,7 +967,7 @@ void Controller::fillManageSection() {
|
||||
if (canViewMembers) {
|
||||
AddButtonWithCount(
|
||||
_controls.buttonsLayout,
|
||||
tr::lng_manage_peer_members(),
|
||||
(_isGroup ? tr::lng_manage_peer_members() : tr::lng_manage_peer_subscribers()),
|
||||
Info::Profile::MigratedOrMeValue(
|
||||
_peer
|
||||
) | rpl::map(
|
||||
|
||||
@@ -888,7 +888,6 @@ void SingleMediaPreview::prepareAnimatedPreview(
|
||||
_gifPreview = Media::Clip::MakeReader(
|
||||
animatedPreviewPath,
|
||||
std::move(callback));
|
||||
if (_gifPreview) _gifPreview->setAutoplay();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1934,6 +1933,7 @@ void SendFilesBox::initSendWay() {
|
||||
if (_albumPreview) {
|
||||
_albumPreview->setSendWay(value);
|
||||
}
|
||||
updateEmojiPanelGeometry();
|
||||
setInnerFocus();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "chat_helpers/stickers.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
@@ -20,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/image/image_location_factory.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/emoji_config.h"
|
||||
#include "lottie/lottie_multi_player.h"
|
||||
@@ -72,6 +74,7 @@ protected:
|
||||
private:
|
||||
struct Element {
|
||||
not_null<DocumentData*> document;
|
||||
std::shared_ptr<Data::DocumentMedia> documentMedia;
|
||||
Lottie::Animation *animated = nullptr;
|
||||
Ui::Animations::Simple overAnimation;
|
||||
};
|
||||
@@ -113,7 +116,7 @@ private:
|
||||
int32 _setHash = 0;
|
||||
MTPDstickerSet::Flags _setFlags = 0;
|
||||
TimeId _setInstallDate = TimeId(0);
|
||||
ImagePtr _setThumbnail;
|
||||
ImageWithLocation _setThumbnail;
|
||||
|
||||
MTPInputStickerSet _input;
|
||||
|
||||
@@ -267,7 +270,7 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
|
||||
continue;
|
||||
}
|
||||
_pack.push_back(document);
|
||||
_elements.push_back({ document });
|
||||
_elements.push_back({ document, document->createMediaView() });
|
||||
}
|
||||
for (const auto &pack : data.vpacks().v) {
|
||||
pack.match([&](const MTPDstickerPack &pack) {
|
||||
@@ -297,25 +300,29 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
|
||||
_setFlags = set.vflags().v;
|
||||
_setInstallDate = set.vinstalled_date().value_or(0);
|
||||
if (const auto thumb = set.vthumb()) {
|
||||
_setThumbnail = Images::Create(set, *thumb);
|
||||
_setThumbnail = Images::FromPhotoSize(
|
||||
&_controller->session(),
|
||||
set,
|
||||
*thumb);
|
||||
} else {
|
||||
_setThumbnail = ImagePtr();
|
||||
_setThumbnail = ImageWithLocation();
|
||||
}
|
||||
auto &sets = _controller->session().data().stickerSetsRef();
|
||||
const auto &sets = _controller->session().data().stickerSets();
|
||||
const auto it = sets.find(_setId);
|
||||
if (it != sets.cend()) {
|
||||
const auto set = it->second.get();
|
||||
using ClientFlag = MTPDstickerSet_ClientFlag;
|
||||
const auto clientFlags = it->flags
|
||||
const auto clientFlags = set->flags
|
||||
& (ClientFlag::f_featured
|
||||
| ClientFlag::f_not_loaded
|
||||
| ClientFlag::f_unread
|
||||
| ClientFlag::f_special);
|
||||
_setFlags |= clientFlags;
|
||||
it->flags = _setFlags;
|
||||
it->installDate = _setInstallDate;
|
||||
it->stickers = _pack;
|
||||
it->emoji = _emoji;
|
||||
it->thumbnail = _setThumbnail;
|
||||
set->flags = _setFlags;
|
||||
set->installDate = _setInstallDate;
|
||||
set->stickers = _pack;
|
||||
set->emoji = _emoji;
|
||||
set->setThumbnail(_setThumbnail);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -357,9 +364,10 @@ void StickerSetBox::Inner::installDone(
|
||||
_setFlags |= MTPDstickerSet::Flag::f_installed_date;
|
||||
auto it = sets.find(_setId);
|
||||
if (it == sets.cend()) {
|
||||
it = sets.insert(
|
||||
it = sets.emplace(
|
||||
_setId,
|
||||
Stickers::Set(
|
||||
std::make_unique<Stickers::Set>(
|
||||
&_controller->session().data(),
|
||||
_setId,
|
||||
_setAccess,
|
||||
_setTitle,
|
||||
@@ -367,14 +375,15 @@ void StickerSetBox::Inner::installDone(
|
||||
_setCount,
|
||||
_setHash,
|
||||
_setFlags,
|
||||
_setInstallDate,
|
||||
_setThumbnail));
|
||||
_setInstallDate)).first;
|
||||
} else {
|
||||
it->flags = _setFlags;
|
||||
it->installDate = _setInstallDate;
|
||||
it->second->flags = _setFlags;
|
||||
it->second->installDate = _setInstallDate;
|
||||
}
|
||||
it->stickers = _pack;
|
||||
it->emoji = _emoji;
|
||||
const auto set = it->second.get();
|
||||
set->setThumbnail(_setThumbnail);
|
||||
set->stickers = _pack;
|
||||
set->emoji = _emoji;
|
||||
|
||||
auto &order = _controller->session().data().stickerSetsOrderRef();
|
||||
int insertAtIndex = 0, currentIndex = order.indexOf(_setId);
|
||||
@@ -385,14 +394,15 @@ void StickerSetBox::Inner::installDone(
|
||||
order.insert(insertAtIndex, _setId);
|
||||
}
|
||||
|
||||
auto custom = sets.find(Stickers::CustomSetId);
|
||||
if (custom != sets.cend()) {
|
||||
for_const (auto sticker, _pack) {
|
||||
const auto customIt = sets.find(Stickers::CustomSetId);
|
||||
if (customIt != sets.cend()) {
|
||||
const auto custom = customIt->second.get();
|
||||
for (const auto sticker : std::as_const(_pack)) {
|
||||
int removeIndex = custom->stickers.indexOf(sticker);
|
||||
if (removeIndex >= 0) custom->stickers.removeAt(removeIndex);
|
||||
}
|
||||
if (custom->stickers.isEmpty()) {
|
||||
sets.erase(custom);
|
||||
sets.erase(customIt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,7 +612,7 @@ void StickerSetBox::Inner::setupLottie(int index) {
|
||||
|
||||
element.animated = Stickers::LottieAnimationFromDocument(
|
||||
getLottiePlayer(),
|
||||
document,
|
||||
element.documentMedia.get(),
|
||||
Stickers::LottieSize::StickerSet,
|
||||
boundingBoxSize() * cIntRetinaFactor());
|
||||
}
|
||||
@@ -621,11 +631,12 @@ void StickerSetBox::Inner::paintSticker(
|
||||
|
||||
const auto &element = _elements[index];
|
||||
const auto document = element.document;
|
||||
document->checkStickerSmall();
|
||||
const auto &media = element.documentMedia;
|
||||
media->checkStickerSmall();
|
||||
|
||||
if (document->sticker()->animated
|
||||
&& !element.animated
|
||||
&& document->loaded()) {
|
||||
&& media->loaded()) {
|
||||
const_cast<Inner*>(this)->setupLottie(index);
|
||||
}
|
||||
|
||||
@@ -650,11 +661,11 @@ void StickerSetBox::Inner::paintSticker(
|
||||
frame);
|
||||
|
||||
_lottiePlayer->unpause(element.animated);
|
||||
} else if (const auto image = document->getStickerSmall()) {
|
||||
} else if (const auto image = media->getStickerSmall()) {
|
||||
p.drawPixmapLeft(
|
||||
ppos,
|
||||
width(),
|
||||
image->pix(document->stickerSetOrigin(), w, h));
|
||||
image->pix(w, h));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,10 +677,11 @@ bool StickerSetBox::Inner::notInstalled() const {
|
||||
if (!_loaded) {
|
||||
return false;
|
||||
}
|
||||
const auto it = _controller->session().data().stickerSets().constFind(_setId);
|
||||
if ((it == _controller->session().data().stickerSets().cend())
|
||||
|| !(it->flags & MTPDstickerSet::Flag::f_installed_date)
|
||||
|| (it->flags & MTPDstickerSet::Flag::f_archived)) {
|
||||
const auto &sets = _controller->session().data().stickerSets();
|
||||
const auto it = sets.find(_setId);
|
||||
if ((it == sets.cend())
|
||||
|| !(it->second->flags & MTPDstickerSet::Flag::f_installed_date)
|
||||
|| (it->second->flags & MTPDstickerSet::Flag::f_archived)) {
|
||||
return !_pack.empty();
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "core/application.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
@@ -165,7 +166,7 @@ void StickersBox::showAttachedStickers() {
|
||||
});
|
||||
|
||||
if (const auto set = Stickers::FeedSet(*setData)) {
|
||||
if (_attached.widget()->appendSet(*set)) {
|
||||
if (_attached.widget()->appendSet(set)) {
|
||||
addedSet = true;
|
||||
if (set->stickers.isEmpty() || (set->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) {
|
||||
_session->api().scheduleStickerSetRequest(set->id, set->access);
|
||||
@@ -220,8 +221,8 @@ void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedSti
|
||||
}
|
||||
if (!setData) continue;
|
||||
|
||||
if (auto set = Stickers::FeedSet(*setData)) {
|
||||
auto index = archived.indexOf(set->id);
|
||||
if (const auto set = Stickers::FeedSet(*setData)) {
|
||||
const auto index = archived.indexOf(set->id);
|
||||
if (archived.isEmpty() || index != archived.size() - 1) {
|
||||
changedSets = true;
|
||||
if (index < archived.size() - 1) {
|
||||
@@ -229,7 +230,7 @@ void StickersBox::getArchivedDone(uint64 offsetId, const MTPmessages_ArchivedSti
|
||||
}
|
||||
archived.push_back(set->id);
|
||||
}
|
||||
if (_archived.widget()->appendSet(*set)) {
|
||||
if (_archived.widget()->appendSet(set)) {
|
||||
addedSet = true;
|
||||
if (set->stickers.isEmpty() || (set->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) {
|
||||
_session->api().scheduleStickerSetRequest(set->id, set->access);
|
||||
@@ -373,17 +374,24 @@ void StickersBox::loadMoreArchived() {
|
||||
}
|
||||
|
||||
uint64 lastId = 0;
|
||||
for (auto setIt = _session->data().archivedStickerSetsOrder().cend(), e = _session->data().archivedStickerSetsOrder().cbegin(); setIt != e;) {
|
||||
const auto &order = _session->data().archivedStickerSetsOrder();
|
||||
const auto &sets = _session->data().stickerSets();
|
||||
for (auto setIt = order.cend(), e = order.cbegin(); setIt != e;) {
|
||||
--setIt;
|
||||
auto it = _session->data().stickerSets().constFind(*setIt);
|
||||
if (it != _session->data().stickerSets().cend()) {
|
||||
if (it->flags & MTPDstickerSet::Flag::f_archived) {
|
||||
lastId = it->id;
|
||||
auto it = sets.find(*setIt);
|
||||
if (it != sets.cend()) {
|
||||
if (it->second->flags & MTPDstickerSet::Flag::f_archived) {
|
||||
lastId = it->second->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_archivedRequestId = MTP::send(MTPmessages_GetArchivedStickers(MTP_flags(0), MTP_long(lastId), MTP_int(kArchivedLimitPerPage)), rpcDone(&StickersBox::getArchivedDone, lastId));
|
||||
_archivedRequestId = MTP::send(
|
||||
MTPmessages_GetArchivedStickers(
|
||||
MTP_flags(0),
|
||||
MTP_long(lastId),
|
||||
MTP_int(kArchivedLimitPerPage)),
|
||||
rpcDone(&StickersBox::getArchivedDone, lastId));
|
||||
}
|
||||
|
||||
void StickersBox::paintEvent(QPaintEvent *e) {
|
||||
@@ -489,13 +497,14 @@ QPixmap StickersBox::grabContentCache() {
|
||||
}
|
||||
|
||||
void StickersBox::installSet(uint64 setId) {
|
||||
auto &sets = _session->data().stickerSetsRef();
|
||||
auto it = sets.find(setId);
|
||||
const auto &sets = _session->data().stickerSets();
|
||||
const auto it = sets.find(setId);
|
||||
if (it == sets.cend()) {
|
||||
rebuildList();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto set = it->second.get();
|
||||
if (_localRemoved.contains(setId)) {
|
||||
_localRemoved.removeOne(setId);
|
||||
if (_installed.widget()) _installed.widget()->setRemovedSets(_localRemoved);
|
||||
@@ -503,11 +512,11 @@ void StickersBox::installSet(uint64 setId) {
|
||||
if (_archived.widget()) _archived.widget()->setRemovedSets(_localRemoved);
|
||||
if (_attached.widget()) _attached.widget()->setRemovedSets(_localRemoved);
|
||||
}
|
||||
if (!(it->flags & MTPDstickerSet::Flag::f_installed_date)
|
||||
|| (it->flags & MTPDstickerSet::Flag::f_archived)) {
|
||||
if (!(set->flags & MTPDstickerSet::Flag::f_installed_date)
|
||||
|| (set->flags & MTPDstickerSet::Flag::f_archived)) {
|
||||
MTP::send(
|
||||
MTPmessages_InstallStickerSet(
|
||||
Stickers::inputSetId(*it),
|
||||
set->mtpInput(),
|
||||
MTP_boolFalse()),
|
||||
rpcDone(&StickersBox::installDone),
|
||||
rpcFail(&StickersBox::installFail, setId));
|
||||
@@ -526,8 +535,8 @@ void StickersBox::installDone(const MTPmessages_StickerSetInstallResult &result)
|
||||
bool StickersBox::installFail(uint64 setId, const RPCError &error) {
|
||||
if (MTP::isDefaultHandledError(error)) return false;
|
||||
|
||||
auto &sets = _session->data().stickerSetsRef();
|
||||
auto it = sets.find(setId);
|
||||
const auto &sets = _session->data().stickerSets();
|
||||
const auto it = sets.find(setId);
|
||||
if (it == sets.cend()) {
|
||||
rebuildList();
|
||||
return true;
|
||||
@@ -550,12 +559,14 @@ void StickersBox::requestArchivedSets() {
|
||||
preloadArchivedSets();
|
||||
}
|
||||
|
||||
auto &sets = _session->data().stickerSets();
|
||||
for_const (auto setId, _session->data().archivedStickerSetsOrder()) {
|
||||
auto it = sets.constFind(setId);
|
||||
const auto &sets = _session->data().stickerSets();
|
||||
const auto &order = _session->data().archivedStickerSetsOrder();
|
||||
for (const auto setId : order) {
|
||||
auto it = sets.find(setId);
|
||||
if (it != sets.cend()) {
|
||||
if (it->stickers.isEmpty() && (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) {
|
||||
_session->api().scheduleStickerSetRequest(setId, it->access);
|
||||
const auto set = it->second.get();
|
||||
if (set->stickers.isEmpty() && (set->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) {
|
||||
_session->api().scheduleStickerSetRequest(setId, set->access);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -624,9 +635,7 @@ void StickersBox::setInnerFocus() {
|
||||
StickersBox::~StickersBox() = default;
|
||||
|
||||
StickersBox::Inner::Row::Row(
|
||||
uint64 id,
|
||||
uint64 accessHash,
|
||||
ImagePtr thumbnail,
|
||||
not_null<Stickers::Set*> set,
|
||||
DocumentData *sticker,
|
||||
int32 count,
|
||||
const QString &title,
|
||||
@@ -638,9 +647,7 @@ StickersBox::Inner::Row::Row(
|
||||
bool removed,
|
||||
int32 pixw,
|
||||
int32 pixh)
|
||||
: id(id)
|
||||
, accessHash(accessHash)
|
||||
, thumbnail(thumbnail)
|
||||
: set(set)
|
||||
, sticker(sticker)
|
||||
, count(count)
|
||||
, title(title)
|
||||
@@ -656,6 +663,10 @@ StickersBox::Inner::Row::Row(
|
||||
|
||||
StickersBox::Inner::Row::~Row() = default;
|
||||
|
||||
bool StickersBox::Inner::Row::isRecentSet() const {
|
||||
return (set->id == Stickers::CloudRecentSetId);
|
||||
}
|
||||
|
||||
StickersBox::Inner::Inner(
|
||||
QWidget *parent,
|
||||
not_null<Main::Session*> session,
|
||||
@@ -804,8 +815,8 @@ QRect StickersBox::Inner::relativeButtonRect(bool removeButton) const {
|
||||
return QRect(buttonx, buttony, buttonw, buttonh);
|
||||
}
|
||||
|
||||
void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> set, int index) {
|
||||
auto xadd = 0, yadd = qRound(set->yadd.current());
|
||||
void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> row, int index) {
|
||||
auto xadd = 0, yadd = qRound(row->yadd.current());
|
||||
if (xadd || yadd) p.translate(xadd, yadd);
|
||||
|
||||
if (_megagroupSet) {
|
||||
@@ -817,8 +828,8 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> set, int index) {
|
||||
}();
|
||||
if (index >= 0 && index == selectedIndex) {
|
||||
p.fillRect(0, 0, width(), _rowHeight, st::contactsBgOver);
|
||||
if (set->ripple) {
|
||||
set->ripple->paint(p, 0, 0, width());
|
||||
if (row->ripple) {
|
||||
row->ripple->paint(p, 0, 0, width());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -833,24 +844,24 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> set, int index) {
|
||||
current = reachedOpacity;
|
||||
}
|
||||
}
|
||||
auto row = myrtlrect(st::contactsPadding.left() / 2, st::contactsPadding.top() / 2, width() - (st::contactsPadding.left() / 2) - _scrollbar - st::contactsPadding.left() / 2, _rowHeight - ((st::contactsPadding.top() + st::contactsPadding.bottom()) / 2));
|
||||
auto rect = myrtlrect(st::contactsPadding.left() / 2, st::contactsPadding.top() / 2, width() - (st::contactsPadding.left() / 2) - _scrollbar - st::contactsPadding.left() / 2, _rowHeight - ((st::contactsPadding.top() + st::contactsPadding.bottom()) / 2));
|
||||
p.setOpacity(current);
|
||||
Ui::Shadow::paint(p, row, width(), st::boxRoundShadow);
|
||||
Ui::Shadow::paint(p, rect, width(), st::boxRoundShadow);
|
||||
p.setOpacity(1);
|
||||
|
||||
App::roundRect(p, row, st::boxBg, BoxCorners);
|
||||
App::roundRect(p, rect, st::boxBg, BoxCorners);
|
||||
|
||||
p.setOpacity(1. - current);
|
||||
paintFakeButton(p, set, index);
|
||||
paintFakeButton(p, row, index);
|
||||
p.setOpacity(1.);
|
||||
} else if (!_megagroupSet) {
|
||||
paintFakeButton(p, set, index);
|
||||
paintFakeButton(p, row, index);
|
||||
}
|
||||
} else if (!_megagroupSet) {
|
||||
paintFakeButton(p, set, index);
|
||||
paintFakeButton(p, row, index);
|
||||
}
|
||||
|
||||
if (set->removed && _section == Section::Installed) {
|
||||
if (row->removed && _section == Section::Installed) {
|
||||
p.setOpacity(st::stickersRowDisabledOpacity);
|
||||
}
|
||||
|
||||
@@ -858,13 +869,13 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> set, int index) {
|
||||
|
||||
if (!_megagroupSet && _section == Section::Installed) {
|
||||
stickerx += st::stickersReorderIcon.width() + st::stickersReorderSkip;
|
||||
if (!set->isRecentSet()) {
|
||||
if (!row->isRecentSet()) {
|
||||
st::stickersReorderIcon.paint(p, st::contactsPadding.left(), (_rowHeight - st::stickersReorderIcon.height()) / 2, width());
|
||||
}
|
||||
}
|
||||
|
||||
if (set->sticker) {
|
||||
paintRowThumbnail(p, set, stickerx);
|
||||
if (row->sticker) {
|
||||
paintRowThumbnail(p, row, stickerx);
|
||||
}
|
||||
|
||||
int namex = stickerx + st::contactsPhotoSize + st::contactsPadding.left();
|
||||
@@ -875,19 +886,19 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> set, int index) {
|
||||
|
||||
p.setFont(st::contactsNameStyle.font);
|
||||
p.setPen(st::contactsNameFg);
|
||||
p.drawTextLeft(namex, namey, width(), set->title, set->titleWidth);
|
||||
p.drawTextLeft(namex, namey, width(), row->title, row->titleWidth);
|
||||
|
||||
if (set->unread) {
|
||||
if (row->unread) {
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::stickersFeaturedUnreadBg);
|
||||
|
||||
{
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawEllipse(style::rtlrect(namex + set->titleWidth + st::stickersFeaturedUnreadSkip, namey + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width()));
|
||||
p.drawEllipse(style::rtlrect(namex + row->titleWidth + st::stickersFeaturedUnreadSkip, namey + st::stickersFeaturedUnreadTop, st::stickersFeaturedUnreadSize, st::stickersFeaturedUnreadSize, width()));
|
||||
}
|
||||
}
|
||||
|
||||
auto statusText = (set->count > 0) ? tr::lng_stickers_count(tr::now, lt_count, set->count) : tr::lng_contacts_loading(tr::now);
|
||||
auto statusText = (row->count > 0) ? tr::lng_stickers_count(tr::now, lt_count, row->count) : tr::lng_contacts_loading(tr::now);
|
||||
|
||||
p.setFont(st::contactsStatusFont);
|
||||
p.setPen(st::contactsStatusFg);
|
||||
@@ -899,30 +910,39 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> set, int index) {
|
||||
|
||||
void StickersBox::Inner::paintRowThumbnail(
|
||||
Painter &p,
|
||||
not_null<Row*> set,
|
||||
not_null<Row*> row,
|
||||
int left) {
|
||||
const auto origin = Data::FileOriginStickerSet(
|
||||
set->id,
|
||||
set->accessHash);
|
||||
const auto thumb = set->thumbnail
|
||||
? set->thumbnail.get()
|
||||
: set->sticker->thumbnail();
|
||||
if (!thumb) {
|
||||
return;
|
||||
row->set->id,
|
||||
row->set->access);
|
||||
if (row->set->hasThumbnail()) {
|
||||
if (!row->thumbnailMedia) {
|
||||
row->thumbnailMedia = row->set->createThumbnailView();
|
||||
row->set->loadThumbnail();
|
||||
}
|
||||
} else if (row->sticker) {
|
||||
if (!row->stickerMedia) {
|
||||
row->stickerMedia = row->sticker->createMediaView();
|
||||
row->stickerMedia->thumbnailWanted(origin);
|
||||
}
|
||||
}
|
||||
thumb->load(origin);
|
||||
validateLottieAnimation(set);
|
||||
if (!set->lottie) {
|
||||
if (!thumb->loaded()) {
|
||||
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 - set->pixw) / 2,
|
||||
st::contactsPadding.top() + (st::contactsPhotoSize - set->pixh) / 2,
|
||||
left + (st::contactsPhotoSize - row->pixw) / 2,
|
||||
st::contactsPadding.top() + (st::contactsPhotoSize - row->pixh) / 2,
|
||||
width(),
|
||||
thumb->pix(origin, set->pixw, set->pixh));
|
||||
} else if (set->lottie->ready()) {
|
||||
const auto frame = set->lottie->frame();
|
||||
thumb->pix(row->pixw, row->pixh));
|
||||
} else if (row->lottie->ready()) {
|
||||
const auto frame = row->lottie->frame();
|
||||
const auto size = frame.size() / cIntRetinaFactor();
|
||||
p.drawImage(
|
||||
QRect(
|
||||
@@ -935,19 +955,21 @@ void StickersBox::Inner::paintRowThumbnail(
|
||||
const auto paused = controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Layer);
|
||||
if (!paused) {
|
||||
set->lottie->markFrameShown();
|
||||
row->lottie->markFrameShown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StickersBox::Inner::validateLottieAnimation(not_null<Row*> set) {
|
||||
if (set->lottie
|
||||
|| !Stickers::HasLottieThumbnail(set->thumbnail, set->sticker)) {
|
||||
void StickersBox::Inner::validateLottieAnimation(not_null<Row*> row) {
|
||||
if (row->lottie
|
||||
|| !Stickers::HasLottieThumbnail(
|
||||
row->thumbnailMedia.get(),
|
||||
row->stickerMedia.get())) {
|
||||
return;
|
||||
}
|
||||
auto player = Stickers::LottieThumbnail(
|
||||
set->thumbnail,
|
||||
set->sticker,
|
||||
row->thumbnailMedia.get(),
|
||||
row->stickerMedia.get(),
|
||||
Stickers::LottieSize::SetsListThumbnail,
|
||||
QSize(
|
||||
st::contactsPhotoSize,
|
||||
@@ -955,21 +977,21 @@ void StickersBox::Inner::validateLottieAnimation(not_null<Row*> set) {
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
set->lottie = std::move(player);
|
||||
set->lottie->updates(
|
||||
row->lottie = std::move(player);
|
||||
row->lottie->updates(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateRowThumbnail(set);
|
||||
updateRowThumbnail(row);
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void StickersBox::Inner::updateRowThumbnail(not_null<Row*> set) {
|
||||
void StickersBox::Inner::updateRowThumbnail(not_null<Row*> row) {
|
||||
const auto rowTop = [&] {
|
||||
if (set == _megagroupSelectedSet.get()) {
|
||||
if (row == _megagroupSelectedSet.get()) {
|
||||
return _megagroupDivider->y() - _rowHeight;
|
||||
}
|
||||
auto top = _itemsTop;
|
||||
for (const auto &row : _rows) {
|
||||
if (row.get() == set) {
|
||||
for (const auto &entry : _rows) {
|
||||
if (entry.get() == row) {
|
||||
return top + qRound(row->yadd.current());
|
||||
}
|
||||
top += _rowHeight;
|
||||
@@ -987,10 +1009,10 @@ void StickersBox::Inner::updateRowThumbnail(not_null<Row*> set) {
|
||||
st::contactsPhotoSize);
|
||||
}
|
||||
|
||||
void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> set, int index) {
|
||||
auto removeButton = (_section == Section::Installed && !set->removed);
|
||||
void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int index) {
|
||||
auto removeButton = (_section == Section::Installed && !row->removed);
|
||||
auto rect = relativeButtonRect(removeButton);
|
||||
if (_section != Section::Installed && set->installed && !set->archived && !set->removed) {
|
||||
if (_section != Section::Installed && row->installed && !row->archived && !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;
|
||||
@@ -999,10 +1021,10 @@ void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> set, int ind
|
||||
auto selected = (index == _actionSel && _actionDown < 0) || (index == _actionDown);
|
||||
if (removeButton) {
|
||||
// Trash icon button when not disabled in Installed.
|
||||
if (set->ripple) {
|
||||
set->ripple->paint(p, rect.x(), rect.y(), width());
|
||||
if (set->ripple->empty()) {
|
||||
set->ripple.reset();
|
||||
if (row->ripple) {
|
||||
row->ripple->paint(p, rect.x(), rect.y(), width());
|
||||
if (row->ripple->empty()) {
|
||||
row->ripple.reset();
|
||||
}
|
||||
}
|
||||
auto &icon = selected ? st::stickersRemove.iconOver : st::stickersRemove.icon;
|
||||
@@ -1018,10 +1040,10 @@ void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> set, int ind
|
||||
auto &text = (_section == Section::Installed) ? _undoText : _addText;
|
||||
auto &textBg = selected ? st.textBgOver : st.textBg;
|
||||
App::roundRect(p, myrtlrect(rect), textBg, ImageRoundRadius::Small);
|
||||
if (set->ripple) {
|
||||
set->ripple->paint(p, rect.x(), rect.y(), width());
|
||||
if (set->ripple->empty()) {
|
||||
set->ripple.reset();
|
||||
if (row->ripple) {
|
||||
row->ripple->paint(p, rect.x(), rect.y(), width());
|
||||
if (row->ripple->empty()) {
|
||||
row->ripple.reset();
|
||||
}
|
||||
}
|
||||
p.setFont(st.font);
|
||||
@@ -1054,19 +1076,19 @@ void StickersBox::Inner::setActionDown(int newActionDown) {
|
||||
}
|
||||
if (_actionDown >= 0 && _actionDown < _rows.size()) {
|
||||
update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight);
|
||||
auto &set = _rows[_actionDown];
|
||||
if (set->ripple) {
|
||||
set->ripple->lastStop();
|
||||
const auto row = _rows[_actionDown].get();
|
||||
if (row->ripple) {
|
||||
row->ripple->lastStop();
|
||||
}
|
||||
}
|
||||
_actionDown = newActionDown;
|
||||
if (_actionDown >= 0 && _actionDown < _rows.size()) {
|
||||
update(0, _itemsTop + _actionDown * _rowHeight, width(), _rowHeight);
|
||||
auto &set = _rows[_actionDown];
|
||||
auto removeButton = (_section == Section::Installed && !set->removed);
|
||||
if (!set->ripple) {
|
||||
const auto row = _rows[_actionDown].get();
|
||||
auto removeButton = (_section == Section::Installed && !row->removed);
|
||||
if (!row->ripple) {
|
||||
if (_section == Section::Installed) {
|
||||
if (set->removed) {
|
||||
if (row->removed) {
|
||||
auto rippleSize = QSize(_undoWidth - st::stickersUndoRemove.width, st::stickersUndoRemove.height);
|
||||
auto rippleMask = Ui::RippleAnimation::roundRectMask(rippleSize, st::buttonRadius);
|
||||
ensureRipple(st::stickersUndoRemove.ripple, std::move(rippleMask), removeButton);
|
||||
@@ -1075,15 +1097,15 @@ void StickersBox::Inner::setActionDown(int newActionDown) {
|
||||
auto rippleMask = Ui::RippleAnimation::ellipseMask(QSize(rippleSize, rippleSize));
|
||||
ensureRipple(st::stickersRemove.ripple, std::move(rippleMask), removeButton);
|
||||
}
|
||||
} else if (!set->installed || set->archived || set->removed) {
|
||||
} else if (!row->installed || row->archived || row->removed) {
|
||||
auto rippleSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height);
|
||||
auto rippleMask = Ui::RippleAnimation::roundRectMask(rippleSize, st::buttonRadius);
|
||||
ensureRipple(st::stickersTrendingAdd.ripple, std::move(rippleMask), removeButton);
|
||||
}
|
||||
}
|
||||
if (set->ripple) {
|
||||
if (row->ripple) {
|
||||
auto rect = relativeButtonRect(removeButton);
|
||||
set->ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(myrtlrect(rect).x(), _itemsTop + _actionDown * _rowHeight + rect.y()));
|
||||
row->ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(myrtlrect(rect).x(), _itemsTop + _actionDown * _rowHeight + rect.y()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1092,14 +1114,6 @@ void StickersBox::Inner::setSelected(SelectedRow selected) {
|
||||
if (_selected == selected) {
|
||||
return;
|
||||
}
|
||||
if ((_megagroupSet || _section != Section::Installed)
|
||||
&& ((_selected.has_value() || _pressed.has_value()) != (selected.has_value() || _pressed.has_value()))) {
|
||||
if (!_inDragArea) {
|
||||
setCursor((selected.has_value() || _pressed.has_value())
|
||||
? style::cur_pointer
|
||||
: style::cur_default);
|
||||
}
|
||||
}
|
||||
auto countSelectedIndex = [&] {
|
||||
if (auto index = base::get_if<int>(&_selected)) {
|
||||
return *index;
|
||||
@@ -1111,6 +1125,7 @@ void StickersBox::Inner::setSelected(SelectedRow selected) {
|
||||
update(0, _itemsTop + selectedIndex * _rowHeight, width(), _rowHeight);
|
||||
}
|
||||
_selected = selected;
|
||||
updateCursor();
|
||||
selectedIndex = countSelectedIndex();
|
||||
if (_megagroupSet && selectedIndex >= 0 && selectedIndex < _rows.size()) {
|
||||
update(0, _itemsTop + selectedIndex * _rowHeight, width(), _rowHeight);
|
||||
@@ -1130,9 +1145,9 @@ void StickersBox::Inner::setPressed(SelectedRow pressed) {
|
||||
auto pressedIndex = countPressedIndex();
|
||||
if (_megagroupSet && pressedIndex >= 0 && pressedIndex < _rows.size()) {
|
||||
update(0, _itemsTop + pressedIndex * _rowHeight, width(), _rowHeight);
|
||||
auto &set = _rows[pressedIndex];
|
||||
if (set->ripple) {
|
||||
set->ripple->lastStop();
|
||||
const auto row = _rows[pressedIndex].get();
|
||||
if (row->ripple) {
|
||||
row->ripple->lastStop();
|
||||
}
|
||||
}
|
||||
_pressed = pressed;
|
||||
@@ -1216,15 +1231,15 @@ void StickersBox::Inner::onUpdateSelected() {
|
||||
auto selectedIndex = floorclamp(local.y() - _itemsTop, _rowHeight, 0, _rows.size() - 1);
|
||||
selected = selectedIndex;
|
||||
local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight);
|
||||
auto &set = _rows[selectedIndex];
|
||||
if (!_megagroupSet && (_section == Section::Installed || !set->installed || set->archived || set->removed)) {
|
||||
auto removeButton = (_section == Section::Installed && !set->removed);
|
||||
const auto row = _rows[selectedIndex].get();
|
||||
if (!_megagroupSet && (_section == Section::Installed || !row->installed || row->archived || row->removed)) {
|
||||
auto removeButton = (_section == Section::Installed && !row->removed);
|
||||
auto rect = myrtlrect(relativeButtonRect(removeButton));
|
||||
actionSel = rect.contains(local) ? selectedIndex : -1;
|
||||
} else {
|
||||
actionSel = -1;
|
||||
}
|
||||
if (!_megagroupSet && _section == Section::Installed && !set->isRecentSet()) {
|
||||
if (!_megagroupSet && _section == Section::Installed && !row->isRecentSet()) {
|
||||
auto dragAreaWidth = st::contactsPadding.left() + st::stickersReorderIcon.width() + st::stickersReorderSkip;
|
||||
auto dragArea = myrtlrect(0, 0, dragAreaWidth, _rowHeight);
|
||||
inDragArea = dragArea.contains(local);
|
||||
@@ -1238,17 +1253,25 @@ void StickersBox::Inner::onUpdateSelected() {
|
||||
setSelected(selected);
|
||||
if (_inDragArea != inDragArea) {
|
||||
_inDragArea = inDragArea;
|
||||
setCursor(_inDragArea
|
||||
? style::cur_sizeall
|
||||
: ((_selected.has_value() || _pressed.has_value())
|
||||
? style::cur_pointer
|
||||
: style::cur_default));
|
||||
updateCursor();
|
||||
}
|
||||
setActionSel(actionSel);
|
||||
emit draggingScrollDelta(0);
|
||||
}
|
||||
}
|
||||
|
||||
void StickersBox::Inner::updateCursor() {
|
||||
setCursor(_inDragArea
|
||||
? style::cur_sizeall
|
||||
: (!_megagroupSet && _section == Section::Installed)
|
||||
? ((_actionSel >= 0 && (_actionDown < 0 || _actionDown == _actionSel))
|
||||
? style::cur_pointer
|
||||
: style::cur_default)
|
||||
: (_selected.has_value() || _pressed.has_value())
|
||||
? style::cur_pointer
|
||||
: style::cur_default);
|
||||
}
|
||||
|
||||
float64 StickersBox::Inner::aboveShadowOpacity() const {
|
||||
if (_above < 0) return 0;
|
||||
|
||||
@@ -1260,9 +1283,7 @@ float64 StickersBox::Inner::aboveShadowOpacity() const {
|
||||
void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
auto pressed = std::exchange(_pressed, SelectedRow());
|
||||
|
||||
if (_section != Section::Installed && !_selected.has_value() && pressed.has_value()) {
|
||||
setCursor(style::cur_default);
|
||||
}
|
||||
updateCursor();
|
||||
|
||||
_mouse = e->globalPos();
|
||||
onUpdateSelected();
|
||||
@@ -1270,7 +1291,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (_section == Section::Installed) {
|
||||
setRowRemoved(_actionDown, !_rows[_actionDown]->removed);
|
||||
} else if (_installSetCallback) {
|
||||
_installSetCallback(_rows[_actionDown]->id);
|
||||
_installSetCallback(_rows[_actionDown]->set->id);
|
||||
}
|
||||
} else if (_dragging >= 0) {
|
||||
QPoint local(mapFromGlobal(_mouse));
|
||||
@@ -1283,42 +1304,28 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
|
||||
_dragging = _started = -1;
|
||||
} else if (pressed == _selected && _actionSel < 0 && _actionDown < 0) {
|
||||
auto selectedIndex = [&] {
|
||||
const auto selectedIndex = [&] {
|
||||
if (auto index = base::get_if<int>(&_selected)) {
|
||||
return *index;
|
||||
}
|
||||
return -1;
|
||||
}();
|
||||
auto getSetByRow = [&](const Row &row) -> const Stickers::Set* {
|
||||
auto &sets = _session->data().stickerSetsRef();
|
||||
if (!row.isRecentSet()) {
|
||||
auto it = sets.find(row.id);
|
||||
if (it != sets.cend()) {
|
||||
return &*it;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
auto showSetByRow = [&](const Row &row) {
|
||||
if (auto set = getSetByRow(row)) {
|
||||
setSelected(SelectedRow());
|
||||
Ui::show(
|
||||
Box<StickerSetBox>(
|
||||
App::wnd()->sessionController(),
|
||||
Stickers::inputSetId(*set)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
const auto showSetByRow = [&](const Row &row) {
|
||||
setSelected(SelectedRow());
|
||||
Ui::show(
|
||||
Box<StickerSetBox>(
|
||||
App::wnd()->sessionController(),
|
||||
row.set->mtpInput()),
|
||||
Ui::LayerOption::KeepOther);
|
||||
};
|
||||
if (selectedIndex >= 0 && !_inDragArea) {
|
||||
auto &row = *_rows[selectedIndex];
|
||||
if (_megagroupSet) {
|
||||
if (auto set = getSetByRow(row)) {
|
||||
setMegagroupSelectedSet(MTP_inputStickerSetID(
|
||||
MTP_long(set->id),
|
||||
MTP_long(set->access)));
|
||||
const auto row = _rows[selectedIndex].get();
|
||||
if (!row->isRecentSet()) {
|
||||
if (_megagroupSet) {
|
||||
setMegagroupSelectedSet(row->set->mtpInput());
|
||||
} else {
|
||||
showSetByRow(*row);
|
||||
}
|
||||
} else {
|
||||
showSetByRow(row);
|
||||
}
|
||||
} else if (_megagroupSelectedSet && _selected.is<MegagroupSet>()) {
|
||||
showSetByRow(*_megagroupSelectedSet);
|
||||
@@ -1429,9 +1436,7 @@ void StickersBox::Inner::setActionSel(int32 actionSel) {
|
||||
if (_actionSel >= 0) update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight);
|
||||
_actionSel = actionSel;
|
||||
if (_actionSel >= 0) update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight);
|
||||
if (_section == Section::Installed) {
|
||||
setCursor((_actionSel >= 0 && (_actionDown < 0 || _actionDown == _actionSel)) ? style::cur_pointer : style::cur_default);
|
||||
}
|
||||
updateCursor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1466,8 +1471,9 @@ void StickersBox::Inner::handleMegagroupSetAddressChange() {
|
||||
auto text = _megagroupSetField->getLastText().trimmed();
|
||||
if (text.isEmpty()) {
|
||||
if (_megagroupSelectedSet) {
|
||||
auto it = _session->data().stickerSets().constFind(_megagroupSelectedSet->id);
|
||||
if (it != _session->data().stickerSets().cend() && !it->shortName.isEmpty()) {
|
||||
const auto &sets = _session->data().stickerSets();
|
||||
const auto it = sets.find(_megagroupSelectedSet->set->id);
|
||||
if (it != sets.cend() && !it->second->shortName.isEmpty()) {
|
||||
setMegagroupSelectedSet(MTP_inputStickerSetEmpty());
|
||||
}
|
||||
}
|
||||
@@ -1477,7 +1483,9 @@ void StickersBox::Inner::handleMegagroupSetAddressChange() {
|
||||
)).done([=](const MTPmessages_StickerSet &result) {
|
||||
_megagroupSetRequestId = 0;
|
||||
auto set = Stickers::FeedSetFull(result);
|
||||
setMegagroupSelectedSet(MTP_inputStickerSetID(MTP_long(set->id), MTP_long(set->access)));
|
||||
setMegagroupSelectedSet(MTP_inputStickerSetID(
|
||||
MTP_long(set->id),
|
||||
MTP_long(set->access)));
|
||||
}).fail([=](const RPCError &error) {
|
||||
_megagroupSetRequestId = 0;
|
||||
setMegagroupSelectedSet(MTP_inputStickerSetEmpty());
|
||||
@@ -1499,32 +1507,33 @@ void StickersBox::Inner::rebuildMegagroupSet() {
|
||||
_megagroupSelectedShadow.destroy();
|
||||
return;
|
||||
}
|
||||
auto &set = _megagroupSetInput.c_inputStickerSetID();
|
||||
auto setId = set.vid().v;
|
||||
auto &sets = _session->data().stickerSets();
|
||||
auto &inputId = _megagroupSetInput.c_inputStickerSetID();
|
||||
auto setId = inputId.vid().v;
|
||||
const auto &sets = _session->data().stickerSets();
|
||||
auto it = sets.find(setId);
|
||||
if (it == sets.cend() || (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) {
|
||||
_session->api().scheduleStickerSetRequest(set.vid().v, set.vaccess_hash().v);
|
||||
if (it == sets.cend()
|
||||
|| (it->second->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) {
|
||||
_session->api().scheduleStickerSetRequest(
|
||||
inputId.vid().v,
|
||||
inputId.vaccess_hash().v);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto set = it->second.get();
|
||||
auto maxNameWidth = countMaxNameWidth();
|
||||
auto titleWidth = 0;
|
||||
auto title = fillSetTitle(*it, maxNameWidth, &titleWidth);
|
||||
auto count = fillSetCount(*it);
|
||||
auto thumbnail = ImagePtr();
|
||||
auto title = fillSetTitle(set, maxNameWidth, &titleWidth);
|
||||
auto count = fillSetCount(set);
|
||||
auto sticker = (DocumentData*)nullptr;
|
||||
auto pixw = 0, pixh = 0;
|
||||
fillSetCover(*it, &thumbnail, &sticker, &pixw, &pixh);
|
||||
fillSetCover(set, &sticker, &pixw, &pixh);
|
||||
auto installed = true, official = false, unread = false, archived = false, removed = false;
|
||||
if (!_megagroupSelectedSet || _megagroupSelectedSet->id != it->id) {
|
||||
_megagroupSetField->setText(it->shortName);
|
||||
if (!_megagroupSelectedSet || _megagroupSelectedSet->set->id != set->id) {
|
||||
_megagroupSetField->setText(set->shortName);
|
||||
_megagroupSetField->finishAnimating();
|
||||
}
|
||||
_megagroupSelectedSet = std::make_unique<Row>(
|
||||
it->id,
|
||||
it->access,
|
||||
thumbnail,
|
||||
set,
|
||||
sticker,
|
||||
count,
|
||||
title,
|
||||
@@ -1561,7 +1570,7 @@ void StickersBox::Inner::rebuild() {
|
||||
auto maxNameWidth = countMaxNameWidth();
|
||||
|
||||
clear();
|
||||
auto &order = ([&]() -> const Stickers::Order & {
|
||||
const auto &order = ([&]() -> const Stickers::Order & {
|
||||
if (_section == Section::Installed) {
|
||||
auto &result = _session->data().stickerSetsOrder();
|
||||
if (_megagroupSet && result.empty()) {
|
||||
@@ -1576,7 +1585,7 @@ void StickersBox::Inner::rebuild() {
|
||||
_rows.reserve(order.size() + 1);
|
||||
_shiftingStartTimes.reserve(order.size() + 1);
|
||||
|
||||
auto &sets = _session->data().stickerSets();
|
||||
const auto &sets = _session->data().stickerSets();
|
||||
if (_megagroupSet) {
|
||||
auto usingFeatured = _session->data().stickerSetsOrder().empty();
|
||||
_megagroupSubTitle->setText(usingFeatured
|
||||
@@ -1584,21 +1593,23 @@ void StickersBox::Inner::rebuild() {
|
||||
: tr::lng_stickers_group_from_your(tr::now));
|
||||
updateControlsGeometry();
|
||||
} else if (_section == Section::Installed) {
|
||||
auto cloudIt = sets.constFind(Stickers::CloudRecentSetId);
|
||||
if (cloudIt != sets.cend() && !cloudIt->stickers.isEmpty()) {
|
||||
rebuildAppendSet(cloudIt.value(), maxNameWidth);
|
||||
auto cloudIt = sets.find(Stickers::CloudRecentSetId);
|
||||
if (cloudIt != sets.cend() && !cloudIt->second->stickers.isEmpty()) {
|
||||
rebuildAppendSet(cloudIt->second.get(), maxNameWidth);
|
||||
}
|
||||
}
|
||||
for_const (auto setId, order) {
|
||||
auto it = sets.constFind(setId);
|
||||
for (const auto setId : order) {
|
||||
auto it = sets.find(setId);
|
||||
if (it == sets.cend()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rebuildAppendSet(it.value(), maxNameWidth);
|
||||
const auto set = it->second.get();
|
||||
rebuildAppendSet(set, maxNameWidth);
|
||||
|
||||
if (it->stickers.isEmpty() || (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) {
|
||||
_session->api().scheduleStickerSetRequest(it->id, it->access);
|
||||
if (set->stickers.isEmpty()
|
||||
|| (set->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) {
|
||||
_session->api().scheduleStickerSetRequest(set->id, set->access);
|
||||
}
|
||||
}
|
||||
_session->api().requestStickerSets();
|
||||
@@ -1626,24 +1637,22 @@ void StickersBox::Inner::updateSize(int newWidth) {
|
||||
|
||||
void StickersBox::Inner::updateRows() {
|
||||
int maxNameWidth = countMaxNameWidth();
|
||||
auto &sets = _session->data().stickerSets();
|
||||
const auto &sets = _session->data().stickerSets();
|
||||
for (const auto &row : _rows) {
|
||||
const auto it = sets.constFind(row->id);
|
||||
const auto it = sets.find(row->set->id);
|
||||
if (it == sets.cend()) {
|
||||
continue;
|
||||
}
|
||||
const auto &set = it.value();
|
||||
const auto set = it->second.get();
|
||||
if (!row->sticker) {
|
||||
auto thumbnail = ImagePtr();
|
||||
auto sticker = (DocumentData*)nullptr;
|
||||
auto pixw = 0, pixh = 0;
|
||||
fillSetCover(set, &thumbnail, &sticker, &pixw, &pixh);
|
||||
fillSetCover(set, &sticker, &pixw, &pixh);
|
||||
if (sticker) {
|
||||
if ((row->thumbnail.get() != thumbnail.get())
|
||||
|| (!thumbnail && row->sticker != sticker)) {
|
||||
if (row->sticker != sticker && !row->thumbnailMedia) {
|
||||
row->lottie = nullptr;
|
||||
row->stickerMedia = nullptr;
|
||||
}
|
||||
row->thumbnail = thumbnail;
|
||||
row->sticker = sticker;
|
||||
row->pixw = pixw;
|
||||
row->pixh = pixh;
|
||||
@@ -1666,9 +1675,9 @@ void StickersBox::Inner::updateRows() {
|
||||
update();
|
||||
}
|
||||
|
||||
bool StickersBox::Inner::appendSet(const Stickers::Set &set) {
|
||||
for_const (auto &row, _rows) {
|
||||
if (row->id == set.id) {
|
||||
bool StickersBox::Inner::appendSet(not_null<Stickers::Set*> set) {
|
||||
for (const auto &row : _rows) {
|
||||
if (row->set == set) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1695,28 +1704,27 @@ int StickersBox::Inner::countMaxNameWidth() const {
|
||||
return namew;
|
||||
}
|
||||
|
||||
void StickersBox::Inner::rebuildAppendSet(const Stickers::Set &set, int maxNameWidth) {
|
||||
void StickersBox::Inner::rebuildAppendSet(
|
||||
not_null<Stickers::Set*> set,
|
||||
int maxNameWidth) {
|
||||
bool installed = true, official = true, unread = false, archived = false, removed = false;
|
||||
if (set.id != Stickers::CloudRecentSetId) {
|
||||
if (set->id != Stickers::CloudRecentSetId) {
|
||||
fillSetFlags(set, &installed, &official, &unread, &archived);
|
||||
}
|
||||
if (_section == Section::Installed && archived) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImagePtr thumbnail;
|
||||
DocumentData *sticker = nullptr;
|
||||
int pixw = 0, pixh = 0;
|
||||
fillSetCover(set, &thumbnail, &sticker, &pixw, &pixh);
|
||||
fillSetCover(set, &sticker, &pixw, &pixh);
|
||||
|
||||
int titleWidth = 0;
|
||||
QString title = fillSetTitle(set, maxNameWidth, &titleWidth);
|
||||
int count = fillSetCount(set);
|
||||
|
||||
_rows.push_back(std::make_unique<Row>(
|
||||
set.id,
|
||||
set.access,
|
||||
thumbnail,
|
||||
set,
|
||||
sticker,
|
||||
count,
|
||||
title,
|
||||
@@ -1731,19 +1739,26 @@ void StickersBox::Inner::rebuildAppendSet(const Stickers::Set &set, int maxNameW
|
||||
_shiftingStartTimes.push_back(0);
|
||||
}
|
||||
|
||||
void StickersBox::Inner::fillSetCover(const Stickers::Set &set, ImagePtr *thumbnail, DocumentData **outSticker, int *outWidth, int *outHeight) const {
|
||||
*thumbnail = set.thumbnail;
|
||||
if (set.stickers.isEmpty()) {
|
||||
void StickersBox::Inner::fillSetCover(
|
||||
not_null<Stickers::Set*> set,
|
||||
DocumentData **outSticker,
|
||||
int *outWidth,
|
||||
int *outHeight) const {
|
||||
if (set->stickers.isEmpty()) {
|
||||
*outSticker = nullptr;
|
||||
*outWidth = *outHeight = 0;
|
||||
return;
|
||||
}
|
||||
auto sticker = *outSticker = set.stickers.front();
|
||||
auto sticker = *outSticker = set->stickers.front();
|
||||
|
||||
const auto size = set.thumbnail
|
||||
? set.thumbnail->size()
|
||||
: sticker->thumbnail()
|
||||
? sticker->thumbnail()->size()
|
||||
const auto size = set->hasThumbnail()
|
||||
? QSize(
|
||||
set->thumbnailLocation().width(),
|
||||
set->thumbnailLocation().height())
|
||||
: sticker->hasThumbnail()
|
||||
? QSize(
|
||||
sticker->thumbnailLocation().width(),
|
||||
sticker->thumbnailLocation().height())
|
||||
: QSize(1, 1);
|
||||
auto pixw = size.width();
|
||||
auto pixh = size.height();
|
||||
@@ -1763,14 +1778,19 @@ void StickersBox::Inner::fillSetCover(const Stickers::Set &set, ImagePtr *thumbn
|
||||
*outHeight = pixh;
|
||||
}
|
||||
|
||||
int StickersBox::Inner::fillSetCount(const Stickers::Set &set) const {
|
||||
int result = set.stickers.isEmpty() ? set.count : set.stickers.size(), added = 0;
|
||||
if (set.id == Stickers::CloudRecentSetId) {
|
||||
auto customIt = _session->data().stickerSets().constFind(Stickers::CustomSetId);
|
||||
if (customIt != _session->data().stickerSets().cend()) {
|
||||
added = customIt->stickers.size();
|
||||
for_const (auto &sticker, Stickers::GetRecentPack()) {
|
||||
if (customIt->stickers.indexOf(sticker.first) < 0) {
|
||||
int StickersBox::Inner::fillSetCount(not_null<Stickers::Set*> set) const {
|
||||
int result = set->stickers.isEmpty()
|
||||
? set->count
|
||||
: set->stickers.size();
|
||||
auto added = 0;
|
||||
if (set->id == Stickers::CloudRecentSetId) {
|
||||
const auto &sets = _session->data().stickerSets();
|
||||
auto customIt = sets.find(Stickers::CustomSetId);
|
||||
if (customIt != sets.cend()) {
|
||||
added = customIt->second->stickers.size();
|
||||
const auto &recent = Stickers::GetRecentPack();
|
||||
for (const auto &sticker : recent) {
|
||||
if (customIt->second->stickers.indexOf(sticker.first) < 0) {
|
||||
++added;
|
||||
}
|
||||
}
|
||||
@@ -1781,8 +1801,11 @@ int StickersBox::Inner::fillSetCount(const Stickers::Set &set) const {
|
||||
return result + added;
|
||||
}
|
||||
|
||||
QString StickersBox::Inner::fillSetTitle(const Stickers::Set &set, int maxNameWidth, int *outTitleWidth) const {
|
||||
auto result = set.title;
|
||||
QString StickersBox::Inner::fillSetTitle(
|
||||
not_null<Stickers::Set*> set,
|
||||
int maxNameWidth,
|
||||
int *outTitleWidth) const {
|
||||
auto result = set->title;
|
||||
int titleWidth = st::contactsNameStyle.font->width(result);
|
||||
if (titleWidth > maxNameWidth) {
|
||||
result = st::contactsNameStyle.font->elided(result, maxNameWidth);
|
||||
@@ -1795,16 +1818,16 @@ QString StickersBox::Inner::fillSetTitle(const Stickers::Set &set, int maxNameWi
|
||||
}
|
||||
|
||||
void StickersBox::Inner::fillSetFlags(
|
||||
const Stickers::Set &set,
|
||||
not_null<Stickers::Set*> set,
|
||||
bool *outInstalled,
|
||||
bool *outOfficial,
|
||||
bool *outUnread,
|
||||
bool *outArchived) {
|
||||
*outInstalled = (set.flags & MTPDstickerSet::Flag::f_installed_date);
|
||||
*outOfficial = (set.flags & MTPDstickerSet::Flag::f_official);
|
||||
*outArchived = (set.flags & MTPDstickerSet::Flag::f_archived);
|
||||
*outInstalled = (set->flags & MTPDstickerSet::Flag::f_installed_date);
|
||||
*outOfficial = (set->flags & MTPDstickerSet::Flag::f_official);
|
||||
*outArchived = (set->flags & MTPDstickerSet::Flag::f_archived);
|
||||
if (_section == Section::Featured) {
|
||||
*outUnread = (set.flags & MTPDstickerSet_ClientFlag::f_unread);
|
||||
*outUnread = (set->flags & MTPDstickerSet_ClientFlag::f_unread);
|
||||
} else {
|
||||
*outUnread = false;
|
||||
}
|
||||
@@ -1816,7 +1839,7 @@ Stickers::Order StickersBox::Inner::collectSets(Check check) const {
|
||||
result.reserve(_rows.size());
|
||||
for_const (auto &row, _rows) {
|
||||
if (check(row.get())) {
|
||||
result.push_back(row->id);
|
||||
result.push_back(row->set->id);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -1843,7 +1866,7 @@ Stickers::Order StickersBox::Inner::getRemovedSets() const {
|
||||
int StickersBox::Inner::getRowIndex(uint64 setId) const {
|
||||
for (auto i = 0, count = int(_rows.size()); i != count; ++i) {
|
||||
auto &row = _rows[i];
|
||||
if (row->id == setId) {
|
||||
if (row->set->id == setId) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -1866,7 +1889,7 @@ void StickersBox::Inner::setFullOrder(const Stickers::Order &order) {
|
||||
|
||||
void StickersBox::Inner::setRemovedSets(const Stickers::Order &removed) {
|
||||
for (auto i = 0, count = int(_rows.size()); i != count; ++i) {
|
||||
setRowRemoved(i, removed.contains(_rows[i]->id));
|
||||
setRowRemoved(i, removed.contains(_rows[i]->set->id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1904,15 +1927,14 @@ void StickersBox::Inner::readVisibleSets() {
|
||||
if (i * _rowHeight < itemsVisibleTop || (i + 1) * _rowHeight > itemsVisibleBottom) {
|
||||
continue;
|
||||
}
|
||||
const auto thumbnail = !_rows[i]->sticker
|
||||
? nullptr
|
||||
: _rows[i]->thumbnail
|
||||
? _rows[i]->thumbnail.get()
|
||||
: _rows[i]->sticker->thumbnail();
|
||||
if (!thumbnail
|
||||
|| thumbnail->loaded()
|
||||
|| _rows[i]->sticker->loaded()) {
|
||||
_session->api().readFeaturedSetDelayed(_rows[i]->id);
|
||||
const auto thumbnailLoading = _rows[i]->set->hasThumbnail()
|
||||
? _rows[i]->set->thumbnailLoading()
|
||||
: _rows[i]->sticker
|
||||
? ((_rows[i]->stickerMedia && _rows[i]->stickerMedia->loaded())
|
||||
|| _rows[i]->sticker->thumbnailLoading())
|
||||
: false;
|
||||
if (!thumbnailLoading || _rows[i]->stickerMedia->loaded()) {
|
||||
_session->api().readFeaturedSetDelayed(_rows[i]->set->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "base/timer.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "chat_helpers/stickers.h"
|
||||
#include "chat_helpers/stickers_set.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/special_fields.h"
|
||||
|
||||
@@ -33,6 +33,18 @@ namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Data {
|
||||
class DocumentMedia;
|
||||
} // namespace Data
|
||||
|
||||
namespace Lottie {
|
||||
class SinglePlayer;
|
||||
} // namespace Lottie
|
||||
|
||||
namespace Stickers {
|
||||
class Set;
|
||||
} // namespace Stickers
|
||||
|
||||
class StickersBox final
|
||||
: public Ui::BoxContent
|
||||
, public RPCSender
|
||||
@@ -173,7 +185,7 @@ public:
|
||||
void rebuild();
|
||||
void updateSize(int newWidth = 0);
|
||||
void updateRows(); // refresh only pack cover stickers
|
||||
bool appendSet(const Stickers::Set &set);
|
||||
bool appendSet(not_null<Stickers::Set*> set);
|
||||
|
||||
Stickers::Order getOrder() const;
|
||||
Stickers::Order getFullOrder() const;
|
||||
@@ -219,9 +231,7 @@ public slots:
|
||||
private:
|
||||
struct Row {
|
||||
Row(
|
||||
uint64 id,
|
||||
uint64 accessHash,
|
||||
ImagePtr thumbnail,
|
||||
not_null<Stickers::Set*> set,
|
||||
DocumentData *sticker,
|
||||
int32 count,
|
||||
const QString &title,
|
||||
@@ -233,15 +243,14 @@ private:
|
||||
bool removed,
|
||||
int32 pixw,
|
||||
int32 pixh);
|
||||
bool isRecentSet() const {
|
||||
return (id == Stickers::CloudRecentSetId);
|
||||
}
|
||||
~Row();
|
||||
|
||||
uint64 id = 0;
|
||||
uint64 accessHash = 0;
|
||||
ImagePtr thumbnail;
|
||||
bool isRecentSet() const;
|
||||
|
||||
const not_null<Stickers::Set*> set;
|
||||
DocumentData *sticker = nullptr;
|
||||
std::shared_ptr<Data::DocumentMedia> stickerMedia;
|
||||
std::shared_ptr<Stickers::SetThumbnailView> thumbnailMedia;
|
||||
int32 count = 0;
|
||||
QString title;
|
||||
int titleWidth = 0;
|
||||
@@ -294,23 +303,24 @@ private:
|
||||
void ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton);
|
||||
|
||||
bool shiftingAnimationCallback(crl::time now);
|
||||
void paintRow(Painter &p, not_null<Row*> set, int index);
|
||||
void paintRowThumbnail(Painter &p, not_null<Row*> set, int left);
|
||||
void paintFakeButton(Painter &p, not_null<Row*> set, int index);
|
||||
void paintRow(Painter &p, not_null<Row*> row, int index);
|
||||
void paintRowThumbnail(Painter &p, not_null<Row*> row, int left);
|
||||
void paintFakeButton(Painter &p, not_null<Row*> row, int index);
|
||||
void clear();
|
||||
void updateCursor();
|
||||
void setActionSel(int32 actionSel);
|
||||
float64 aboveShadowOpacity() const;
|
||||
void validateLottieAnimation(not_null<Row*> set);
|
||||
void updateRowThumbnail(not_null<Row*> set);
|
||||
void validateLottieAnimation(not_null<Row*> row);
|
||||
void updateRowThumbnail(not_null<Row*> row);
|
||||
|
||||
void readVisibleSets();
|
||||
|
||||
void updateControlsGeometry();
|
||||
void rebuildAppendSet(const Stickers::Set &set, int maxNameWidth);
|
||||
void fillSetCover(const Stickers::Set &set, ImagePtr *thumbnail, DocumentData **outSticker, int *outWidth, int *outHeight) const;
|
||||
int fillSetCount(const Stickers::Set &set) const;
|
||||
QString fillSetTitle(const Stickers::Set &set, int maxNameWidth, int *outTitleWidth) const;
|
||||
void fillSetFlags(const Stickers::Set &set, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outArchived);
|
||||
void rebuildAppendSet(not_null<Stickers::Set*> set, int maxNameWidth);
|
||||
void fillSetCover(not_null<Stickers::Set*> set, DocumentData **outSticker, int *outWidth, int *outHeight) const;
|
||||
int fillSetCount(not_null<Stickers::Set*> set) const;
|
||||
QString fillSetTitle(not_null<Stickers::Set*> set, int maxNameWidth, int *outTitleWidth) const;
|
||||
void fillSetFlags(not_null<Stickers::Set*> set, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outArchived);
|
||||
void rebuildMegagroupSet();
|
||||
void fixupMegagroupSetAddress();
|
||||
void handleMegagroupSetAddressChange();
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "mtproto/facade.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
bool actionSelected) override;
|
||||
|
||||
private:
|
||||
void refreshStatus();
|
||||
void refreshStatus() override;
|
||||
static Type ComputeType(not_null<const HistoryItem*> item);
|
||||
|
||||
std::vector<not_null<HistoryItem*>> _items;
|
||||
|
||||
@@ -18,23 +18,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "media/audio/media_audio_track.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "calls/calls_panel.h"
|
||||
#include "calls/calls_controller.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_session.h"
|
||||
#include "facades.h"
|
||||
|
||||
#ifdef slots
|
||||
#undef slots
|
||||
#define NEED_TO_RESTORE_SLOTS
|
||||
#endif // slots
|
||||
|
||||
#include <VoIPController.h>
|
||||
#include <VoIPServerConfig.h>
|
||||
|
||||
#ifdef NEED_TO_RESTORE_SLOTS
|
||||
#define slots Q_SLOTS
|
||||
#undef NEED_TO_RESTORE_SLOTS
|
||||
#endif // NEED_TO_RESTORE_SLOTS
|
||||
|
||||
namespace Calls {
|
||||
namespace {
|
||||
|
||||
@@ -43,25 +31,25 @@ constexpr auto kHangupTimeoutMs = 5000;
|
||||
constexpr auto kSha256Size = 32;
|
||||
|
||||
void AppendEndpoint(
|
||||
std::vector<tgvoip::Endpoint> &list,
|
||||
std::vector<TgVoipEndpoint> &list,
|
||||
const MTPPhoneConnection &connection) {
|
||||
connection.match([&](const MTPDphoneConnection &data) {
|
||||
if (data.vpeer_tag().v.length() != 16) {
|
||||
return;
|
||||
}
|
||||
const auto ipv4 = tgvoip::IPv4Address(std::string(
|
||||
data.vip().v.constData(),
|
||||
data.vip().v.size()));
|
||||
const auto ipv6 = tgvoip::IPv6Address(std::string(
|
||||
data.vipv6().v.constData(),
|
||||
data.vipv6().v.size()));
|
||||
list.emplace_back(
|
||||
(int64_t)data.vid().v,
|
||||
(uint16_t)data.vport().v,
|
||||
ipv4,
|
||||
ipv6,
|
||||
tgvoip::Endpoint::Type::UDP_RELAY,
|
||||
(unsigned char*)data.vpeer_tag().v.data());
|
||||
auto endpoint = TgVoipEndpoint{
|
||||
.endpointId = (int64_t)data.vid().v,
|
||||
.host = TgVoipEdpointHost{
|
||||
.ipv4 = data.vip().v.toStdString(),
|
||||
.ipv6 = data.vipv6().v.toStdString() },
|
||||
.port = (uint16_t)data.vport().v,
|
||||
.type = TgVoipEndpointType::UdpRelay
|
||||
};
|
||||
const auto tag = data.vpeer_tag().v;
|
||||
if (tag.size() >= 16) {
|
||||
memcpy(endpoint.peerTag, tag.data(), 16);
|
||||
}
|
||||
list.push_back(std::move(endpoint));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -80,47 +68,25 @@ uint64 ComputeFingerprint(bytes::const_span authKey) {
|
||||
| (gsl::to_integer<uint64>(hash[12]));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Call::ControllerPointer::create() {
|
||||
Expects(_data == nullptr);
|
||||
|
||||
_data = std::make_unique<tgvoip::VoIPController>();
|
||||
[[nodiscard]] std::vector<std::string> CollectVersions() {
|
||||
return { TgVoip::getVersion() };
|
||||
}
|
||||
|
||||
void Call::ControllerPointer::reset() {
|
||||
if (const auto controller = base::take(_data)) {
|
||||
controller->Stop();
|
||||
[[nodiscard]] QVector<MTPstring> WrapVersions(
|
||||
const std::vector<std::string> &data) {
|
||||
auto result = QVector<MTPstring>();
|
||||
result.reserve(data.size());
|
||||
for (const auto &version : data) {
|
||||
result.push_back(MTP_string(version));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Call::ControllerPointer::empty() const {
|
||||
return (_data == nullptr);
|
||||
[[nodiscard]] QVector<MTPstring> CollectVersionsForApi() {
|
||||
return WrapVersions(CollectVersions());
|
||||
}
|
||||
|
||||
bool Call::ControllerPointer::operator==(std::nullptr_t) const {
|
||||
return empty();
|
||||
}
|
||||
|
||||
Call::ControllerPointer::operator bool() const {
|
||||
return !empty();
|
||||
}
|
||||
|
||||
tgvoip::VoIPController *Call::ControllerPointer::operator->() const {
|
||||
Expects(!empty());
|
||||
|
||||
return _data.get();
|
||||
}
|
||||
|
||||
tgvoip::VoIPController &Call::ControllerPointer::operator*() const {
|
||||
Expects(!empty());
|
||||
|
||||
return *_data;
|
||||
}
|
||||
|
||||
Call::ControllerPointer::~ControllerPointer() {
|
||||
reset();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Call::Delegate::~Delegate() = default;
|
||||
|
||||
@@ -199,8 +165,8 @@ void Call::startOutgoing() {
|
||||
MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p
|
||||
| MTPDphoneCallProtocol::Flag::f_udp_reflector),
|
||||
MTP_int(kMinLayer),
|
||||
MTP_int(tgvoip::VoIPController::GetConnectionMaxLayer()),
|
||||
MTP_vector(1, MTP_string("2.4.4")))
|
||||
MTP_int(TgVoip::getConnectionMaxLayer()),
|
||||
MTP_vector(CollectVersionsForApi()))
|
||||
)).done([=](const MTPphone_PhoneCall &result) {
|
||||
Expects(result.type() == mtpc_phone_phoneCall);
|
||||
|
||||
@@ -278,8 +244,8 @@ void Call::actuallyAnswer() {
|
||||
MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p
|
||||
| MTPDphoneCallProtocol::Flag::f_udp_reflector),
|
||||
MTP_int(kMinLayer),
|
||||
MTP_int(tgvoip::VoIPController::GetConnectionMaxLayer()),
|
||||
MTP_vector(1, MTP_string("2.4.4")))
|
||||
MTP_int(TgVoip::getConnectionMaxLayer()),
|
||||
MTP_vector(CollectVersionsForApi()))
|
||||
)).done([=](const MTPphone_PhoneCall &result) {
|
||||
Expects(result.type() == mtpc_phone_phoneCall);
|
||||
auto &call = result.c_phone_phoneCall();
|
||||
@@ -300,7 +266,7 @@ void Call::actuallyAnswer() {
|
||||
void Call::setMute(bool mute) {
|
||||
_mute = mute;
|
||||
if (_controller) {
|
||||
_controller->SetMicMute(_mute);
|
||||
_controller->setMuteMicrophone(_mute);
|
||||
}
|
||||
_muteChanged.notify(_mute);
|
||||
}
|
||||
@@ -334,8 +300,7 @@ void Call::redial() {
|
||||
}
|
||||
|
||||
QString Call::getDebugLog() const {
|
||||
const auto debug = _controller->GetDebugString();
|
||||
return QString::fromUtf8(debug.data(), debug.size());
|
||||
return QString::fromStdString(_controller->getDebugInfo());
|
||||
}
|
||||
|
||||
void Call::startWaitingTrack() {
|
||||
@@ -441,7 +406,9 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
|
||||
return false;
|
||||
}
|
||||
if (data.is_need_debug()) {
|
||||
auto debugLog = _controller ? _controller->GetDebugLog() : std::string();
|
||||
auto debugLog = _controller
|
||||
? _controller->getDebugInfo()
|
||||
: std::string();
|
||||
if (!debugLog.empty()) {
|
||||
MTP::send(
|
||||
MTPphone_SaveCallDebug(
|
||||
@@ -517,8 +484,8 @@ void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) {
|
||||
MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p
|
||||
| MTPDphoneCallProtocol::Flag::f_udp_reflector),
|
||||
MTP_int(kMinLayer),
|
||||
MTP_int(tgvoip::VoIPController::GetConnectionMaxLayer()),
|
||||
MTP_vector(1, MTP_string("2.4.4")))
|
||||
MTP_int(TgVoip::getConnectionMaxLayer()),
|
||||
MTP_vector(CollectVersionsForApi()))
|
||||
)).done([this](const MTPphone_PhoneCall &result) {
|
||||
Expects(result.type() == mtpc_phone_phoneCall);
|
||||
|
||||
@@ -566,126 +533,123 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
|
||||
return;
|
||||
}
|
||||
|
||||
tgvoip::VoIPController::Config config;
|
||||
config.dataSaving = tgvoip::DATA_SAVING_NEVER;
|
||||
const auto &protocol = call.vprotocol().c_phoneCallProtocol();
|
||||
|
||||
TgVoipConfig config;
|
||||
config.dataSaving = TgVoipDataSaving::Never;
|
||||
config.enableAEC = !Platform::IsMac10_7OrGreater();
|
||||
config.enableNS = true;
|
||||
config.enableAGC = true;
|
||||
config.enableVolumeControl = true;
|
||||
config.initTimeout = Global::CallConnectTimeoutMs() / 1000;
|
||||
config.recvTimeout = Global::CallPacketTimeoutMs() / 1000;
|
||||
config.initializationTimeout = Global::CallConnectTimeoutMs() / 1000.;
|
||||
config.receiveTimeout = Global::CallPacketTimeoutMs() / 1000.;
|
||||
config.enableP2P = call.is_p2p_allowed();
|
||||
config.maxApiLayer = protocol.vmax_layer().v;
|
||||
if (Logs::DebugEnabled()) {
|
||||
auto callLogFolder = cWorkingDir() + qsl("DebugLogs");
|
||||
auto callLogPath = callLogFolder + qsl("/last_call_log.txt");
|
||||
auto callLogNative = QDir::toNativeSeparators(callLogPath);
|
||||
#ifdef Q_OS_WIN
|
||||
config.logFilePath = callLogNative.toStdWString();
|
||||
config.logPath = callLogNative.toStdWString();
|
||||
#else // Q_OS_WIN
|
||||
const auto callLogUtf = QFile::encodeName(callLogNative);
|
||||
config.logFilePath.resize(callLogUtf.size());
|
||||
ranges::copy(callLogUtf, config.logFilePath.begin());
|
||||
config.logPath.resize(callLogUtf.size());
|
||||
ranges::copy(callLogUtf, config.logPath.begin());
|
||||
#endif // Q_OS_WIN
|
||||
QFile(callLogPath).remove();
|
||||
QDir().mkpath(callLogFolder);
|
||||
}
|
||||
|
||||
const auto &protocol = call.vprotocol().c_phoneCallProtocol();
|
||||
auto endpoints = std::vector<tgvoip::Endpoint>();
|
||||
auto endpoints = std::vector<TgVoipEndpoint>();
|
||||
for (const auto &connection : call.vconnections().v) {
|
||||
AppendEndpoint(endpoints, connection);
|
||||
}
|
||||
|
||||
auto callbacks = tgvoip::VoIPController::Callbacks();
|
||||
callbacks.connectionStateChanged = [](
|
||||
tgvoip::VoIPController *controller,
|
||||
int state) {
|
||||
const auto call = static_cast<Call*>(controller->implData);
|
||||
call->handleControllerStateChange(controller, state);
|
||||
};
|
||||
callbacks.signalBarCountChanged = [](
|
||||
tgvoip::VoIPController *controller,
|
||||
int count) {
|
||||
const auto call = static_cast<Call*>(controller->implData);
|
||||
call->handleControllerBarCountChange(controller, count);
|
||||
};
|
||||
|
||||
_controller.create();
|
||||
if (_mute) {
|
||||
_controller->SetMicMute(_mute);
|
||||
}
|
||||
_controller->implData = static_cast<void*>(this);
|
||||
_controller->SetRemoteEndpoints(
|
||||
endpoints,
|
||||
call.is_p2p_allowed(),
|
||||
protocol.vmax_layer().v);
|
||||
_controller->SetConfig(config);
|
||||
_controller->SetCurrentAudioOutput(Global::CallOutputDeviceID().toStdString());
|
||||
_controller->SetCurrentAudioInput(Global::CallInputDeviceID().toStdString());
|
||||
_controller->SetOutputVolume(Global::CallOutputVolume()/100.0f);
|
||||
_controller->SetInputVolume(Global::CallInputVolume()/100.0f);
|
||||
#ifdef Q_OS_MAC
|
||||
_controller->SetAudioOutputDuckingEnabled(Global::CallAudioDuckingEnabled());
|
||||
#endif
|
||||
_controller->SetEncryptionKey(reinterpret_cast<char*>(_authKey.data()), (_type == Type::Outgoing));
|
||||
_controller->SetCallbacks(callbacks);
|
||||
auto proxy = TgVoipProxy();
|
||||
if (Global::UseProxyForCalls()
|
||||
&& (Global::ProxySettings() == MTP::ProxyData::Settings::Enabled)) {
|
||||
const auto &proxy = Global::SelectedProxy();
|
||||
if (proxy.supportsCalls()) {
|
||||
Assert(proxy.type == MTP::ProxyData::Type::Socks5);
|
||||
_controller->SetProxy(
|
||||
tgvoip::PROXY_SOCKS5,
|
||||
proxy.host.toStdString(),
|
||||
proxy.port,
|
||||
proxy.user.toStdString(),
|
||||
proxy.password.toStdString());
|
||||
const auto &selected = Global::SelectedProxy();
|
||||
if (selected.supportsCalls()) {
|
||||
Assert(selected.type == MTP::ProxyData::Type::Socks5);
|
||||
proxy.host = selected.host.toStdString();
|
||||
proxy.port = selected.port;
|
||||
proxy.login = selected.user.toStdString();
|
||||
proxy.password = selected.password.toStdString();
|
||||
}
|
||||
}
|
||||
_controller->Start();
|
||||
_controller->Connect();
|
||||
|
||||
auto encryptionKey = TgVoipEncryptionKey();
|
||||
encryptionKey.isOutgoing = (_type == Type::Outgoing);
|
||||
encryptionKey.value = ranges::view::all(
|
||||
_authKey
|
||||
) | ranges::view::transform([](bytes::type byte) {
|
||||
return static_cast<uint8_t>(byte);
|
||||
}) | ranges::to_vector;
|
||||
|
||||
_controller = MakeController(
|
||||
"2.4.4",
|
||||
config,
|
||||
TgVoipPersistentState(),
|
||||
endpoints,
|
||||
proxy.host.empty() ? nullptr : &proxy,
|
||||
TgVoipNetworkType::Unknown,
|
||||
encryptionKey);
|
||||
|
||||
const auto raw = _controller.get();
|
||||
raw->setOnStateUpdated([=](TgVoipState state) {
|
||||
handleControllerStateChange(raw, state);
|
||||
});
|
||||
raw->setOnSignalBarsUpdated([=](int count) {
|
||||
handleControllerBarCountChange(count);
|
||||
});
|
||||
if (_mute) {
|
||||
raw->setMuteMicrophone(_mute);
|
||||
}
|
||||
raw->setAudioOutputDevice(
|
||||
Global::CallOutputDeviceID().toStdString());
|
||||
raw->setAudioInputDevice(
|
||||
Global::CallInputDeviceID().toStdString());
|
||||
raw->setOutputVolume(Global::CallOutputVolume() / 100.0f);
|
||||
raw->setInputVolume(Global::CallInputVolume() / 100.0f);
|
||||
raw->setAudioOutputDuckingEnabled(Global::CallAudioDuckingEnabled());
|
||||
}
|
||||
|
||||
void Call::handleControllerStateChange(
|
||||
tgvoip::VoIPController *controller,
|
||||
int state) {
|
||||
not_null<Controller*> controller,
|
||||
TgVoipState state) {
|
||||
// NB! Can be called from an arbitrary thread!
|
||||
// This can be called from ~VoIPController()!
|
||||
// Expects(controller == _controller.get());
|
||||
Expects(controller->implData == static_cast<void*>(this));
|
||||
|
||||
switch (state) {
|
||||
case tgvoip::STATE_WAIT_INIT: {
|
||||
case TgVoipState::WaitInit: {
|
||||
DEBUG_LOG(("Call Info: State changed to WaitingInit."));
|
||||
setStateQueued(State::WaitingInit);
|
||||
} break;
|
||||
|
||||
case tgvoip::STATE_WAIT_INIT_ACK: {
|
||||
case TgVoipState::WaitInitAck: {
|
||||
DEBUG_LOG(("Call Info: State changed to WaitingInitAck."));
|
||||
setStateQueued(State::WaitingInitAck);
|
||||
} break;
|
||||
|
||||
case tgvoip::STATE_ESTABLISHED: {
|
||||
case TgVoipState::Established: {
|
||||
DEBUG_LOG(("Call Info: State changed to Established."));
|
||||
setStateQueued(State::Established);
|
||||
} break;
|
||||
|
||||
case tgvoip::STATE_FAILED: {
|
||||
auto error = controller->GetLastError();
|
||||
case TgVoipState::Failed: {
|
||||
auto error = QString::fromStdString(controller->getLastError());
|
||||
LOG(("Call Info: State changed to Failed, error: %1.").arg(error));
|
||||
setFailedQueued(error);
|
||||
} break;
|
||||
|
||||
default: LOG(("Call Error: Unexpected state in handleStateChange: %1").arg(state));
|
||||
default: LOG(("Call Error: Unexpected state in handleStateChange: %1"
|
||||
).arg(int(state)));
|
||||
}
|
||||
}
|
||||
|
||||
void Call::handleControllerBarCountChange(
|
||||
tgvoip::VoIPController *controller,
|
||||
int count) {
|
||||
void Call::handleControllerBarCountChange(int count) {
|
||||
// NB! Can be called from an arbitrary thread!
|
||||
// This can be called from ~VoIPController()!
|
||||
// Expects(controller == _controller.get());
|
||||
Expects(controller->implData == static_cast<void*>(this));
|
||||
|
||||
crl::on_main(this, [=] {
|
||||
setSignalBarCount(count);
|
||||
@@ -790,32 +754,30 @@ void Call::setState(State state) {
|
||||
}
|
||||
}
|
||||
|
||||
void Call::setCurrentAudioDevice(bool input, std::string deviceID){
|
||||
void Call::setCurrentAudioDevice(bool input, std::string deviceID) {
|
||||
if (_controller) {
|
||||
if (input) {
|
||||
_controller->SetCurrentAudioInput(deviceID);
|
||||
_controller->setAudioInputDevice(deviceID);
|
||||
} else {
|
||||
_controller->SetCurrentAudioOutput(deviceID);
|
||||
_controller->setAudioOutputDevice(deviceID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Call::setAudioVolume(bool input, float level){
|
||||
void Call::setAudioVolume(bool input, float level) {
|
||||
if (_controller) {
|
||||
if(input) {
|
||||
_controller->SetInputVolume(level);
|
||||
if (input) {
|
||||
_controller->setInputVolume(level);
|
||||
} else {
|
||||
_controller->SetOutputVolume(level);
|
||||
_controller->setOutputVolume(level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Call::setAudioDuckingEnabled(bool enabled){
|
||||
#ifdef Q_OS_MAC
|
||||
void Call::setAudioDuckingEnabled(bool enabled) {
|
||||
if (_controller) {
|
||||
_controller->SetAudioOutputDuckingEnabled(enabled);
|
||||
_controller->setAudioOutputDuckingEnabled(enabled);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
|
||||
@@ -844,7 +806,7 @@ void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
|
||||
|
||||
setState(hangupState);
|
||||
auto duration = getDurationMs() / 1000;
|
||||
auto connectionId = _controller ? _controller->GetPreferredRelayID() : 0;
|
||||
auto connectionId = _controller ? _controller->getPreferredRelayId() : 0;
|
||||
_finishByTimeoutTimer.call(kHangupTimeoutMs, [this, finalState] { setState(finalState); });
|
||||
_api.request(MTPphone_DiscardCall(
|
||||
MTP_flags(0),
|
||||
@@ -870,7 +832,7 @@ void Call::setStateQueued(State state) {
|
||||
});
|
||||
}
|
||||
|
||||
void Call::setFailedQueued(int error) {
|
||||
void Call::setFailedQueued(const QString &error) {
|
||||
crl::on_main(this, [=] {
|
||||
handleControllerError(error);
|
||||
});
|
||||
@@ -887,13 +849,13 @@ void Call::handleRequestError(const RPCError &error) {
|
||||
finish(FinishType::Failed);
|
||||
}
|
||||
|
||||
void Call::handleControllerError(int error) {
|
||||
if (error == tgvoip::ERROR_INCOMPATIBLE) {
|
||||
void Call::handleControllerError(const QString &error) {
|
||||
if (error == u"ERROR_INCOMPATIBLE"_q) {
|
||||
Ui::show(Box<InformBox>(
|
||||
Lang::Hard::CallErrorIncompatible().replace(
|
||||
"{user}",
|
||||
_user->name)));
|
||||
} else if (error == tgvoip::ERROR_AUDIO_IO) {
|
||||
} else if (error == u"ERROR_AUDIO_IO"_q) {
|
||||
Ui::show(Box<InformBox>(tr::lng_call_error_audio_io(tr::now)));
|
||||
}
|
||||
finish(FinishType::Failed);
|
||||
@@ -912,8 +874,8 @@ Call::~Call() {
|
||||
destroyController();
|
||||
}
|
||||
|
||||
void UpdateConfig(const std::string& data) {
|
||||
tgvoip::ServerConfig::GetSharedInstance()->Update(data);
|
||||
void UpdateConfig(const std::string &data) {
|
||||
TgVoip::setGlobalServerConfig(data);
|
||||
}
|
||||
|
||||
} // namespace Calls
|
||||
|
||||
@@ -19,12 +19,12 @@ class Track;
|
||||
} // namespace Audio
|
||||
} // namespace Media
|
||||
|
||||
namespace tgvoip {
|
||||
class VoIPController;
|
||||
} // namespace tgvoip
|
||||
enum class TgVoipState;
|
||||
|
||||
namespace Calls {
|
||||
|
||||
class Controller;
|
||||
|
||||
struct DhConfig {
|
||||
int32 version = 0;
|
||||
int32 g = 0;
|
||||
@@ -129,30 +129,13 @@ public:
|
||||
~Call();
|
||||
|
||||
private:
|
||||
class ControllerPointer {
|
||||
public:
|
||||
void create();
|
||||
void reset();
|
||||
bool empty() const;
|
||||
|
||||
bool operator==(std::nullptr_t) const;
|
||||
explicit operator bool() const;
|
||||
tgvoip::VoIPController *operator->() const;
|
||||
tgvoip::VoIPController &operator*() const;
|
||||
|
||||
~ControllerPointer();
|
||||
|
||||
private:
|
||||
std::unique_ptr<tgvoip::VoIPController> _data;
|
||||
|
||||
};
|
||||
enum class FinishType {
|
||||
None,
|
||||
Ended,
|
||||
Failed,
|
||||
};
|
||||
void handleRequestError(const RPCError &error);
|
||||
void handleControllerError(int error);
|
||||
void handleControllerError(const QString &error);
|
||||
void finish(FinishType type, const MTPPhoneCallDiscardReason &reason = MTP_phoneCallDiscardReasonDisconnect());
|
||||
void startOutgoing();
|
||||
void startIncoming();
|
||||
@@ -160,11 +143,9 @@ private:
|
||||
|
||||
void generateModExpFirst(bytes::const_span randomSeed);
|
||||
void handleControllerStateChange(
|
||||
tgvoip::VoIPController *controller,
|
||||
int state);
|
||||
void handleControllerBarCountChange(
|
||||
tgvoip::VoIPController *controller,
|
||||
int count);
|
||||
not_null<Controller*> controller,
|
||||
TgVoipState state);
|
||||
void handleControllerBarCountChange(int count);
|
||||
void createAndStartController(const MTPDphoneCall &call);
|
||||
|
||||
template <typename T>
|
||||
@@ -177,7 +158,7 @@ private:
|
||||
void startConfirmedCall(const MTPDphoneCall &call);
|
||||
void setState(State state);
|
||||
void setStateQueued(State state);
|
||||
void setFailedQueued(int error);
|
||||
void setFailedQueued(const QString &error);
|
||||
void setSignalBarCount(int count);
|
||||
void destroyController();
|
||||
|
||||
@@ -210,12 +191,12 @@ private:
|
||||
uint64 _accessHash = 0;
|
||||
uint64 _keyFingerprint = 0;
|
||||
|
||||
ControllerPointer _controller;
|
||||
std::unique_ptr<Controller> _controller;
|
||||
|
||||
std::unique_ptr<Media::Audio::Track> _waitingTrack;
|
||||
|
||||
};
|
||||
|
||||
void UpdateConfig(const std::string& data);
|
||||
void UpdateConfig(const std::string &data);
|
||||
|
||||
} // namespace Calls
|
||||
|
||||
31
Telegram/SourceFiles/calls/calls_controller.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
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 "calls/calls_controller.h"
|
||||
|
||||
#include "calls/calls_controller_tgvoip.h"
|
||||
|
||||
namespace Calls {
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Controller> MakeController(
|
||||
const std::string &version,
|
||||
const TgVoipConfig &config,
|
||||
const TgVoipPersistentState &persistentState,
|
||||
const std::vector<TgVoipEndpoint> &endpoints,
|
||||
const TgVoipProxy *proxy,
|
||||
TgVoipNetworkType initialNetworkType,
|
||||
const TgVoipEncryptionKey &encryptionKey) {
|
||||
return std::make_unique<TgVoipController>(
|
||||
config,
|
||||
persistentState,
|
||||
endpoints,
|
||||
proxy,
|
||||
initialNetworkType,
|
||||
encryptionKey);
|
||||
}
|
||||
|
||||
} // namespace Calls
|
||||