Compare commits
236 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3a2460a54 | ||
|
|
04212140cc | ||
|
|
4e1904b137 | ||
|
|
0299ba4873 | ||
|
|
46ce0df832 | ||
|
|
d66debd802 | ||
|
|
454fe8cdf7 | ||
|
|
c4dfc634d0 | ||
|
|
b08fa069b4 | ||
|
|
3d20958bb4 | ||
|
|
c693fcb2b0 | ||
|
|
ddad42d80e | ||
|
|
c6f66e83ee | ||
|
|
8bb3b7fada | ||
|
|
1d24d29afa | ||
|
|
0536a479f9 | ||
|
|
7fef7e6315 | ||
|
|
c6ef2b057e | ||
|
|
784f10678c | ||
|
|
92dbd7089b | ||
|
|
415990c913 | ||
|
|
e42af74dd2 | ||
|
|
bd1a46252d | ||
|
|
874e5e0a61 | ||
|
|
81457693f1 | ||
|
|
3a700650be | ||
|
|
d642c3f3b5 | ||
|
|
4be03ffc25 | ||
|
|
dcac3146c7 | ||
|
|
10012d6b31 | ||
|
|
3aa1b1e9ae | ||
|
|
4e8a1f8d29 | ||
|
|
11e03a181d | ||
|
|
4e366dc86e | ||
|
|
015277c4d3 | ||
|
|
745bbfe268 | ||
|
|
e8a58c4c8d | ||
|
|
d4f2c96322 | ||
|
|
bd1d0417a9 | ||
|
|
4a78eb100a | ||
|
|
7fa5ca192b | ||
|
|
371510cfe2 | ||
|
|
622c1a910b | ||
|
|
ce3279143d | ||
|
|
3e6ba53a04 | ||
|
|
41cb37b091 | ||
|
|
264dd0c1d2 | ||
|
|
163e549708 | ||
|
|
035a19b41e | ||
|
|
6ac5f32796 | ||
|
|
0c4c4b2fcf | ||
|
|
a106d6e804 | ||
|
|
6b7c33f0ee | ||
|
|
8d2cacac80 | ||
|
|
cc9eb7f893 | ||
|
|
94c2969f8b | ||
|
|
719bed6e85 | ||
|
|
8634c1f7f3 | ||
|
|
def1266216 | ||
|
|
2e02f27a5c | ||
|
|
04855f1697 | ||
|
|
c29730650e | ||
|
|
6257445d5e | ||
|
|
405c8125da | ||
|
|
62da24c20b | ||
|
|
e8df47c926 | ||
|
|
433c147dd0 | ||
|
|
8f4fdb6d0d | ||
|
|
aecdc01e41 | ||
|
|
bdce2d5e25 | ||
|
|
5968219fe4 | ||
|
|
f81271d1fe | ||
|
|
9f3af7234e | ||
|
|
90c0929407 | ||
|
|
1e31cda78d | ||
|
|
f7e4f18e9b | ||
|
|
0fb42ed82a | ||
|
|
c535a7c564 | ||
|
|
c32f2e71e8 | ||
|
|
a38f7b357c | ||
|
|
438f69e1b2 | ||
|
|
891b4a91a3 | ||
|
|
889139f31f | ||
|
|
247b1f64ca | ||
|
|
13ad590a51 | ||
|
|
e021e0beb3 | ||
|
|
b097bd7225 | ||
|
|
27f85df562 | ||
|
|
e484bc78d0 | ||
|
|
d89be1d1d4 | ||
|
|
ab429212e5 | ||
|
|
f53f934001 | ||
|
|
a287dec242 | ||
|
|
f73b0f0b0d | ||
|
|
61d89113d4 | ||
|
|
7862443fcb | ||
|
|
1a40f2b3ef | ||
|
|
4c1213ce9e | ||
|
|
afbc0c498f | ||
|
|
a91c078fb1 | ||
|
|
6eedeb3852 | ||
|
|
8af559e711 | ||
|
|
7f928a92ea | ||
|
|
22dc7601f5 | ||
|
|
9abca29f4c | ||
|
|
cf48152853 | ||
|
|
883c3ecf65 | ||
|
|
9a96298ef7 | ||
|
|
a1a845dbf1 | ||
|
|
40e925d3f9 | ||
|
|
808e8dcf4f | ||
|
|
c2bb2526d3 | ||
|
|
59abe95754 | ||
|
|
040f29abe6 | ||
|
|
e9dffe78e3 | ||
|
|
3a51303fb0 | ||
|
|
008a301755 | ||
|
|
be14456290 | ||
|
|
4a94a0c438 | ||
|
|
608d8307d9 | ||
|
|
6f9ea1cc01 | ||
|
|
47170da813 | ||
|
|
01ab6e6d4d | ||
|
|
b8424b1d89 | ||
|
|
78d83a2c69 | ||
|
|
beb623bee2 | ||
|
|
d42ce87c09 | ||
|
|
60002555c3 | ||
|
|
fb20be3e6c | ||
|
|
31e1ed216a | ||
|
|
ce91caa820 | ||
|
|
95a579f25f | ||
|
|
9fe82480e1 | ||
|
|
17549ad5ea | ||
|
|
f22a804220 | ||
|
|
c563df7d9d | ||
|
|
1849f01b15 | ||
|
|
f8b83dd186 | ||
|
|
f0e1d2fd02 | ||
|
|
734d834a20 | ||
|
|
b3eb41b989 | ||
|
|
45419205c6 | ||
|
|
204645a715 | ||
|
|
50a0429786 | ||
|
|
00cdae0369 | ||
|
|
437c9320cd | ||
|
|
0888901d79 | ||
|
|
55edb3bdfe | ||
|
|
fcdc4cd465 | ||
|
|
49c230b898 | ||
|
|
883a62c0a2 | ||
|
|
58008ab7b0 | ||
|
|
94468ecf6d | ||
|
|
91118bf087 | ||
|
|
6805085bbc | ||
|
|
78d874e9a3 | ||
|
|
8d5e356733 | ||
|
|
4c2779bbaf | ||
|
|
70c993774a | ||
|
|
f128665f6b | ||
|
|
0cd68f866d | ||
|
|
242ced4022 | ||
|
|
23aef6c365 | ||
|
|
210e3f0cb6 | ||
|
|
d86b4659d6 | ||
|
|
f1cf6b4896 | ||
|
|
8fd1253266 | ||
|
|
5991cd4350 | ||
|
|
309372164c | ||
|
|
f1cdc7e3f9 | ||
|
|
dfad68a0b8 | ||
|
|
0f887b3432 | ||
|
|
077f0c393e | ||
|
|
7d29f9ce17 | ||
|
|
6635d03818 | ||
|
|
e523492de0 | ||
|
|
c77f1bf082 | ||
|
|
3c8c059447 | ||
|
|
612e0d4a10 | ||
|
|
78a2835bbf | ||
|
|
ea8e256a23 | ||
|
|
2f2de84b43 | ||
|
|
ead6892857 | ||
|
|
2b642d4da9 | ||
|
|
a474074705 | ||
|
|
d34eabdc11 | ||
|
|
f9be304e54 | ||
|
|
39b0662a2c | ||
|
|
ae59de7652 | ||
|
|
b7f5cfe083 | ||
|
|
97076dbf83 | ||
|
|
7b1c47ff2e | ||
|
|
21c578cf2e | ||
|
|
4d2041ae48 | ||
|
|
4672e3d068 | ||
|
|
ade7745b0b | ||
|
|
c7881ae4a3 | ||
|
|
ba89242759 | ||
|
|
0139390c71 | ||
|
|
693d3a922f | ||
|
|
ebf8a20d0d | ||
|
|
41c98a6c49 | ||
|
|
aa87d627c9 | ||
|
|
e782e065a0 | ||
|
|
f36240eb38 | ||
|
|
72861a6409 | ||
|
|
6e3fb253b9 | ||
|
|
8a34f29329 | ||
|
|
2574389129 | ||
|
|
3a66d317ee | ||
|
|
ecdee8812d | ||
|
|
0e00bbb012 | ||
|
|
3d8b691ff9 | ||
|
|
57f8b03949 | ||
|
|
6dfefa3f21 | ||
|
|
eaae7b1d03 | ||
|
|
d02092f09e | ||
|
|
c529974da5 | ||
|
|
0e1f59a0ed | ||
|
|
fe97939abf | ||
|
|
8ce798db12 | ||
|
|
f801cb822e | ||
|
|
1dcbb103a8 | ||
|
|
d97880913c | ||
|
|
47673bba50 | ||
|
|
580e6baee6 | ||
|
|
f56af090e0 | ||
|
|
95c0c400c7 | ||
|
|
170ec16f39 | ||
|
|
786bedf271 | ||
|
|
1de1747c38 | ||
|
|
7bb4e5e4a9 | ||
|
|
5dad293335 | ||
|
|
e4ac70090e | ||
|
|
bc7925985c | ||
|
|
a911f2c0c3 |
41
.github/workflows/linux.yml
vendored
@@ -54,6 +54,7 @@ jobs:
|
||||
defines:
|
||||
- ""
|
||||
- "DESKTOP_APP_DISABLE_DBUS_INTEGRATION"
|
||||
- "TDESKTOP_DISABLE_GTK_INTEGRATION"
|
||||
|
||||
env:
|
||||
GIT: "https://github.com"
|
||||
@@ -95,7 +96,8 @@ jobs:
|
||||
libgtk2.0-dev libice-dev libsm-dev libicu-dev libdrm-dev dh-autoreconf \
|
||||
autoconf automake build-essential libxml2-dev libass-dev libfreetype6-dev \
|
||||
libgpac-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev \
|
||||
libvorbis-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-screensaver0-dev \
|
||||
libvorbis-dev libxcb1-dev libxcb-image0-dev libxcb-shm0-dev \
|
||||
libxcb-screensaver0-dev libjpeg-dev ninja-build \
|
||||
libxcb-xfixes0-dev libxcb-keysyms1-dev libxcb-icccm4-dev libatspi2.0-dev \
|
||||
libxcb-render-util0-dev libxcb-util0-dev libxcb-xkb-dev libxrender-dev \
|
||||
libasound-dev libpulse-dev libxcb-sync0-dev libxcb-randr0-dev libegl1-mesa-dev \
|
||||
@@ -220,7 +222,6 @@ jobs:
|
||||
--disable-network \
|
||||
--disable-autodetect \
|
||||
--disable-everything \
|
||||
--disable-neon \
|
||||
--disable-alsa \
|
||||
--disable-iconv \
|
||||
--enable-libopus \
|
||||
@@ -373,7 +374,7 @@ jobs:
|
||||
make -j$(nproc)
|
||||
sudo make DESTDIR="$LibrariesPath/openssl-cache" install_sw
|
||||
cd ..
|
||||
rm -rf $opensslDir
|
||||
# rm -rf $opensslDir # Keep this folder for WebRTC.
|
||||
- name: OpenSSL install.
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
@@ -442,6 +443,7 @@ jobs:
|
||||
-qt-harfbuzz \
|
||||
-qt-pcre \
|
||||
-qt-xcb \
|
||||
-no-icu \
|
||||
-no-gtk \
|
||||
-static \
|
||||
-dbus-runtime \
|
||||
@@ -512,6 +514,39 @@ jobs:
|
||||
mkdir -p breakpad/out/Default/
|
||||
cp breakpad-cache/dump_syms breakpad/out/Default/dump_syms
|
||||
|
||||
- name: WebRTC cache.
|
||||
id: cache-webrtc
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/tg_owt
|
||||
key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}
|
||||
- name: WebRTC.
|
||||
if: steps.cache-webrtc.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
|
||||
git clone $GIT/desktop-app/tg_owt.git
|
||||
mkdir -p tg_owt/out/Debug
|
||||
cd tg_owt/out/Debug
|
||||
cmake -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DTG_OWT_SPECIAL_TARGET=linux \
|
||||
-DTG_OWT_LIBJPEG_INCLUDE_PATH=`pwd`/../../../qt_$QT/qtbase/src/3rdparty/libjpeg \
|
||||
-DTG_OWT_OPENSSL_INCLUDE_PATH=$OPENSSL_PREFIX/include \
|
||||
-DTG_OWT_OPUS_INCLUDE_PATH=/usr/local/include/opus \
|
||||
-DTG_OWT_FFMPEG_INCLUDE_PATH=/usr/local/include \
|
||||
../..
|
||||
ninja
|
||||
|
||||
# Cleanup.
|
||||
cd $LibrariesPath/tg_owt
|
||||
mv out/Debug/libtg_owt.a libtg_owt.a
|
||||
rm -rf out
|
||||
mkdir -p out/Debug
|
||||
mv libtg_owt.a out/Debug/libtg_owt.a
|
||||
|
||||
rm -rf $LibrariesPath/openssl_${OPENSSL_VER}
|
||||
|
||||
- name: Telegram Desktop build.
|
||||
if: env.ONLY_CACHE == 'false'
|
||||
run: |
|
||||
|
||||
59
.github/workflows/mac.yml
vendored
@@ -154,7 +154,7 @@ jobs:
|
||||
$MIN_MAC
|
||||
make build_libs -j$(nproc)
|
||||
|
||||
SSL_DIR=$LibrariesPath/openssl_${{ env.OPENSSL_VER }}
|
||||
SSL_DIR=$LibrariesPath/openssl_$OPENSSL_VER
|
||||
mkdir -p $SSL_DIR/include
|
||||
copyLib() {
|
||||
cp $1.a $SSL_DIR/$1.a
|
||||
@@ -167,29 +167,33 @@ jobs:
|
||||
id: cache-opus
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/opus
|
||||
path: ${{ env.LibrariesPath }}/opus-cache
|
||||
key: ${{ runner.OS }}-opus-${{ env.CACHE_KEY }}
|
||||
- name: Opus.
|
||||
- name: Opus clone.
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
|
||||
git clone -b v1.3 --depth=1 $GIT/xiph/opus
|
||||
- name: Opus build.
|
||||
if: steps.cache-opus.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
|
||||
git clone $GIT/xiph/opus
|
||||
cd opus
|
||||
git checkout v1.3
|
||||
./autogen.sh
|
||||
CFLAGS="$MIN_MAC $UNGUARDED" CPPFLAGS="$MIN_MAC $UNGUARDED" LDFLAGS="$MIN_MAC" ./configure --prefix=$PREFIX
|
||||
make -j$(nproc)
|
||||
sudo make DESTDIR="$LibrariesPath/opus-cache" install
|
||||
- name: Opus install.
|
||||
run: |
|
||||
cd $LibrariesPath/opus
|
||||
sudo make install
|
||||
cd $LibrariesPath
|
||||
sudo cp -R opus-cache/. /
|
||||
|
||||
- name: Libiconv cache.
|
||||
id: cache-libiconv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/${{ env.LIBICONV_VER }}
|
||||
path: ${{ env.LibrariesPath }}/libiconv-cache
|
||||
key: ${{ runner.OS }}-${{ env.LIBICONV_VER }}-${{ env.CACHE_KEY }}
|
||||
- name: Libiconv.
|
||||
if: steps.cache-libiconv.outputs.cache-hit != 'true'
|
||||
@@ -201,10 +205,11 @@ jobs:
|
||||
cd $LIBICONV_VER
|
||||
CFLAGS="$MIN_MAC $UNGUARDED" CPPFLAGS="$MIN_MAC $UNGUARDED" LDFLAGS="$MIN_MAC" ./configure --enable-static --prefix=$PREFIX
|
||||
make -j$(nproc)
|
||||
sudo make DESTDIR="$LibrariesPath/libiconv-cache" install
|
||||
- name: Libiconv install.
|
||||
run: |
|
||||
cd $LibrariesPath/$LIBICONV_VER
|
||||
sudo make install
|
||||
cd $LibrariesPath
|
||||
sudo cp -R libiconv-cache/. /
|
||||
|
||||
- name: FFmpeg cache.
|
||||
id: cache-ffmpeg
|
||||
@@ -419,8 +424,8 @@ jobs:
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
|
||||
git clone git://code.qt.io/qt/qt5.git qt$QT
|
||||
cd qt$QT
|
||||
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.8
|
||||
git submodule update qtbase
|
||||
@@ -449,6 +454,36 @@ jobs:
|
||||
make clean
|
||||
cp -r $QT_PREFIX $LibrariesPath/qt-cache
|
||||
|
||||
- name: WebRTC cache.
|
||||
id: cache-webrtc
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/tg_owt
|
||||
key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}
|
||||
- name: WebRTC.
|
||||
if: steps.cache-webrtc.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd $LibrariesPath
|
||||
|
||||
git clone $GIT/desktop-app/tg_owt.git
|
||||
mkdir -p tg_owt/out/Debug
|
||||
cd tg_owt/out/Debug
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DTG_OWT_SPECIAL_TARGET=mac \
|
||||
-DTG_OWT_LIBJPEG_INCLUDE_PATH=`pwd`/../../../qt_$QT/qtbase/src/3rdparty/libjpeg \
|
||||
-DTG_OWT_OPENSSL_INCLUDE_PATH=`pwd`/../../../openssl_$OPENSSL_VER/include \
|
||||
-DTG_OWT_OPUS_INCLUDE_PATH=$PREFIX/include/opus \
|
||||
-DTG_OWT_FFMPEG_INCLUDE_PATH=/usr/local/include \
|
||||
../..
|
||||
ninja
|
||||
|
||||
# Cleanup.
|
||||
cd $LibrariesPath/tg_owt
|
||||
mv out/Debug/libtg_owt.a libtg_owt.a
|
||||
rm -rf out
|
||||
mkdir -p out/Debug
|
||||
mv libtg_owt.a out/Debug/libtg_owt.a
|
||||
|
||||
- name: Telegram Desktop build.
|
||||
if: env.ONLY_CACHE == 'false'
|
||||
run: |
|
||||
|
||||
16
.github/workflows/snap.yml
vendored
@@ -41,8 +41,8 @@ on:
|
||||
jobs:
|
||||
|
||||
linux:
|
||||
name: Ubuntu 20.04
|
||||
runs-on: ubuntu-20.04
|
||||
name: Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
@@ -55,18 +55,22 @@ jobs:
|
||||
|
||||
- name: First set up.
|
||||
run: |
|
||||
# Workaround for permanent problems with third-party repository keys
|
||||
sudo rm -rf /etc/apt/sources.list.d/*
|
||||
sudo apt-get purge --autoremove lxd
|
||||
|
||||
sudo apt-get update
|
||||
sudo snap install --classic snapcraft
|
||||
sudo snap install lxd
|
||||
|
||||
# Workaround for snapcraft
|
||||
# See https://forum.snapcraft.io/t/13258
|
||||
sudo chown root:root /
|
||||
|
||||
sudo usermod -aG lxd $USER
|
||||
|
||||
sudo snap run lxd init --auto
|
||||
sudo snap run lxd waitready
|
||||
|
||||
- name: Telegram Desktop snap build.
|
||||
run: sudo snap run snapcraft --destructive-mode
|
||||
run: sg lxd -c 'snap run snapcraft --use-lxd'
|
||||
|
||||
- name: Move artifact.
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
|
||||
91
.github/workflows/win.yml
vendored
@@ -60,6 +60,7 @@ jobs:
|
||||
VC: "call vcvars32.bat && cd Libraries"
|
||||
GIT: "https://github.com"
|
||||
QT: "5_12_8"
|
||||
QT_VER: "5.12.8"
|
||||
OPENSSL_VER: "1_1_1"
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
ONLY_CACHE: "false"
|
||||
@@ -121,6 +122,20 @@ jobs:
|
||||
cd Patches
|
||||
eval $checkoutCommit
|
||||
|
||||
- name: Find any version of Python 2.
|
||||
shell: cmd
|
||||
run: |
|
||||
echo Find any version of Python 2.
|
||||
for /D %%a in (C:\hostedtoolcache\windows\Python\2.*) do (
|
||||
SET PY2=%%a\x64
|
||||
)
|
||||
if [%PY2%] == [] (
|
||||
echo Python 2 is not found.
|
||||
exit 1
|
||||
)
|
||||
echo Found %PY2%.
|
||||
echo ::set-env name=PY2::%PY2%
|
||||
|
||||
- name: LZMA.
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -154,7 +169,7 @@ jobs:
|
||||
move ossl_static.pdb out32.dbg\ossl_static
|
||||
nmake clean
|
||||
move out32.dbg\ossl_static out32.dbg\ossl_static.pdb
|
||||
perl Configure no-shared VC-WIN32
|
||||
perl Configure no-shared no-tests VC-WIN32
|
||||
nmake
|
||||
mkdir out32
|
||||
move libcrypto.lib out32
|
||||
@@ -191,11 +206,12 @@ jobs:
|
||||
cd openal-soft
|
||||
git checkout fix_capture
|
||||
cd build
|
||||
cmake ^
|
||||
cmake .. ^
|
||||
-G "Visual Studio 16 2019" ^
|
||||
-A Win32 ^
|
||||
-D LIBTYPE:STRING=STATIC ^
|
||||
-D FORCE_STATIC_VCRT:STRING=ON ..
|
||||
-D FORCE_STATIC_VCRT=ON ^
|
||||
-D ALSOFT_BACKEND_WASAPI=OFF
|
||||
|
||||
msbuild -m OpenAL.vcxproj /property:Configuration=Debug
|
||||
|
||||
@@ -214,16 +230,6 @@ jobs:
|
||||
run: |
|
||||
cd %LibrariesPath%
|
||||
|
||||
echo Find any version of Python 2.
|
||||
for /D %%a in (C:\hostedtoolcache\windows\Python\2.*) do (
|
||||
SET PY2=%%a\x64
|
||||
)
|
||||
IF [%PY2%] == [] (
|
||||
echo Python 2 is not found.
|
||||
exit 1
|
||||
)
|
||||
echo Found %PY2%.
|
||||
|
||||
git clone %GIT%/telegramdesktop/gyp.git
|
||||
cd gyp
|
||||
SET PATH=%PY2%;%cd%;%PATH%
|
||||
@@ -293,7 +299,7 @@ jobs:
|
||||
id: cache-qt
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/Qt-5.12.8
|
||||
path: ${{ env.LibrariesPath }}/Qt-${{ env.QT_VER }}
|
||||
key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_5_12_8/*') }}
|
||||
- name: Configure Qt 5.12.8.
|
||||
if: steps.cache-qt.outputs.cache-hit != 'true'
|
||||
@@ -304,18 +310,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.8
|
||||
git checkout v%QT_VER%
|
||||
git submodule update qtbase
|
||||
git submodule update qtimageformats
|
||||
cd qtbase
|
||||
for /r %%i in (..\..\patches\qtbase_%QT%\*) do git apply %%i
|
||||
cd ..
|
||||
|
||||
SET SSL=%LibrariesPath%\openssl_1_1_1
|
||||
SET SSL=%LibrariesPath%\openssl_%OPENSSL_VER%
|
||||
SET LIBS=libcrypto.lib Ws2_32.lib Gdi32.lib Advapi32.lib Crypt32.lib User32.lib
|
||||
|
||||
configure ^
|
||||
-prefix "%LibrariesPath%\Qt-5.12.8" ^
|
||||
-prefix "%LibrariesPath%\Qt-%QT_VER%" ^
|
||||
-debug ^
|
||||
-force-debug-info ^
|
||||
-opensource ^
|
||||
@@ -343,6 +349,57 @@ jobs:
|
||||
cd ..
|
||||
rmdir /S /Q qt_%QT%
|
||||
|
||||
- name: WebRTC cache.
|
||||
id: cache-webrtc
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LibrariesPath }}/tg_owt
|
||||
key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}
|
||||
- name: WebRTC.
|
||||
if: steps.cache-webrtc.outputs.cache-hit != 'true'
|
||||
shell: cmd
|
||||
run: |
|
||||
%VC%
|
||||
|
||||
:: Qt libjpeg.
|
||||
mkdir qt_%QT%
|
||||
cd qt_%QT%
|
||||
git clone -b %QT_VER% https://github.com/qt/qtbase
|
||||
|
||||
move qtbase\src\3rdparty\libjpeg ..
|
||||
cd ..
|
||||
dir
|
||||
rmdir /S /Q qt_%QT%
|
||||
mkdir qt_%QT%\qtbase\src\3rdparty\
|
||||
move libjpeg qt_%QT%\qtbase\src\3rdparty\
|
||||
|
||||
:: WebRTC.
|
||||
cd %LibrariesPath%
|
||||
|
||||
git clone %GIT%/desktop-app/tg_owt.git
|
||||
mkdir tg_owt\out\Debug
|
||||
cd tg_owt\out\Debug
|
||||
cmake -G Ninja ^
|
||||
-DCMAKE_BUILD_TYPE=Debug ^
|
||||
-DTG_OWT_SPECIAL_TARGET=win ^
|
||||
-DTG_OWT_LIBJPEG_INCLUDE_PATH=%cd%/../../../qt_%QT%/qtbase/src/3rdparty/libjpeg ^
|
||||
-DTG_OWT_OPENSSL_INCLUDE_PATH=%cd%/../../../openssl_%OPENSSL_VER%/include ^
|
||||
-DTG_OWT_OPUS_INCLUDE_PATH=%cd%/../../../opus/include ^
|
||||
-DTG_OWT_FFMPEG_INCLUDE_PATH=%cd%/../../../ffmpeg ^
|
||||
../..
|
||||
|
||||
ninja
|
||||
|
||||
:: Cleanup.
|
||||
cd %LibrariesPath%\tg_owt
|
||||
move out\Debug\tg_owt.lib tg_owt.lib
|
||||
rmdir /S /Q out
|
||||
mkdir out\Debug
|
||||
move tg_owt.lib out\Debug\tg_owt.lib
|
||||
|
||||
cd %LibrariesPath%
|
||||
rmdir /S /Q qt_%QT%
|
||||
|
||||
- name: Read defines.
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
3
.gitmodules
vendored
@@ -1,9 +1,6 @@
|
||||
[submodule "Telegram/ThirdParty/libtgvoip"]
|
||||
path = Telegram/ThirdParty/libtgvoip
|
||||
url = https://github.com/telegramdesktop/libtgvoip
|
||||
[submodule "Telegram/ThirdParty/variant"]
|
||||
path = Telegram/ThirdParty/variant
|
||||
url = https://github.com/desktop-app/variant.git
|
||||
[submodule "Telegram/ThirdParty/GSL"]
|
||||
path = Telegram/ThirdParty/GSL
|
||||
url = https://github.com/Microsoft/GSL.git
|
||||
|
||||
@@ -36,5 +36,9 @@ include(cmake/options.cmake)
|
||||
|
||||
include(cmake/external/qt/package.cmake)
|
||||
|
||||
set(desktop_app_skip_libs
|
||||
variant
|
||||
)
|
||||
|
||||
add_subdirectory(cmake)
|
||||
add_subdirectory(Telegram)
|
||||
|
||||
@@ -34,6 +34,7 @@ Version **1.8.15** was the last that supports older systems
|
||||
|
||||
* 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))
|
||||
* WebRTC ([New BSD License](https://github.com/desktop-app/tg_owt/blob/master/src/LICENSE))
|
||||
* 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/))
|
||||
@@ -45,7 +46,6 @@ Version **1.8.15** was the last that supports older systems
|
||||
* Opus codec ([BSD License](http://www.opus-codec.org/license/))
|
||||
* FFmpeg ([LGPL](https://www.ffmpeg.org/legal.html))
|
||||
* Guideline Support Library ([MIT License](https://github.com/Microsoft/GSL/blob/master/LICENSE))
|
||||
* 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))
|
||||
|
||||
@@ -21,21 +21,21 @@ add_subdirectory(lib_qr)
|
||||
add_subdirectory(lib_webrtc)
|
||||
add_subdirectory(codegen)
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(lib_ui/cmake/generate_styles.cmake)
|
||||
include(cmake/generate_lang.cmake)
|
||||
include(cmake/generate_numbers.cmake)
|
||||
|
||||
get_filename_component(src_loc SourceFiles REALPATH)
|
||||
get_filename_component(res_loc Resources REALPATH)
|
||||
|
||||
include(cmake/telegram_options.cmake)
|
||||
include(cmake/lib_export.cmake)
|
||||
include(cmake/lib_ffmpeg.cmake)
|
||||
include(cmake/lib_mtproto.cmake)
|
||||
include(cmake/lib_scheme.cmake)
|
||||
include(cmake/lib_tgvoip.cmake)
|
||||
include(cmake/lib_tgcalls.cmake)
|
||||
include(cmake/td_export.cmake)
|
||||
include(cmake/td_mtproto.cmake)
|
||||
include(cmake/td_lang.cmake)
|
||||
include(cmake/td_scheme.cmake)
|
||||
include(cmake/td_ui.cmake)
|
||||
|
||||
set(style_files
|
||||
boxes/boxes.style
|
||||
@@ -61,14 +61,45 @@ set(dependent_style_files
|
||||
${submodules_loc}/lib_ui/ui/basic.style
|
||||
${submodules_loc}/lib_ui/ui/layers/layers.style
|
||||
${submodules_loc}/lib_ui/ui/widgets/widgets.style
|
||||
${src_loc}/ui/td_common.style
|
||||
)
|
||||
|
||||
generate_styles(Telegram ${src_loc} "${style_files}" "${dependent_style_files}")
|
||||
generate_lang(Telegram ${res_loc}/langs/lang.strings)
|
||||
generate_numbers(Telegram ${res_loc}/numbers.txt)
|
||||
|
||||
set_target_properties(Telegram PROPERTIES AUTOMOC ON AUTORCC ON)
|
||||
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
tdesktop::lib_tgcalls_legacy
|
||||
tdesktop::lib_tgcalls
|
||||
tdesktop::lib_tgvoip
|
||||
tdesktop::td_export
|
||||
tdesktop::td_mtproto
|
||||
tdesktop::td_lang
|
||||
tdesktop::td_scheme
|
||||
tdesktop::td_ui
|
||||
desktop-app::lib_webrtc
|
||||
desktop-app::lib_base
|
||||
desktop-app::lib_crl
|
||||
desktop-app::lib_ui
|
||||
desktop-app::lib_tl
|
||||
desktop-app::lib_storage
|
||||
desktop-app::lib_lottie
|
||||
desktop-app::lib_qr
|
||||
desktop-app::lib_ffmpeg
|
||||
desktop-app::external_lz4
|
||||
desktop-app::external_rlottie
|
||||
desktop-app::external_zlib
|
||||
desktop-app::external_minizip
|
||||
desktop-app::external_qt
|
||||
desktop-app::external_qr_code_generator
|
||||
desktop-app::external_crash_reports
|
||||
desktop-app::external_auto_updates
|
||||
desktop-app::external_openssl
|
||||
desktop-app::external_openal
|
||||
)
|
||||
|
||||
if (LINUX)
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
@@ -95,52 +126,17 @@ if (LINUX)
|
||||
desktop-app::external_hime_qt
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (add_hunspell_library)
|
||||
target_link_libraries(Telegram PRIVATE desktop-app::external_hunspell)
|
||||
endif()
|
||||
if (DESKTOP_APP_USE_PACKAGED AND Qt5WaylandClient_VERSION VERSION_LESS 5.13.0)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client)
|
||||
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
tdesktop::lib_tgcalls_legacy
|
||||
tdesktop::lib_tgcalls
|
||||
tdesktop::lib_tgvoip
|
||||
tdesktop::lib_mtproto
|
||||
tdesktop::lib_scheme
|
||||
tdesktop::lib_export
|
||||
desktop-app::lib_webrtc
|
||||
desktop-app::lib_base
|
||||
desktop-app::lib_crl
|
||||
desktop-app::lib_ui
|
||||
desktop-app::lib_tl
|
||||
desktop-app::lib_storage
|
||||
desktop-app::lib_lottie
|
||||
desktop-app::lib_qr
|
||||
desktop-app::lib_ffmpeg
|
||||
desktop-app::external_lz4
|
||||
desktop-app::external_rlottie
|
||||
desktop-app::external_zlib
|
||||
desktop-app::external_minizip
|
||||
desktop-app::external_qt
|
||||
desktop-app::external_qr_code_generator
|
||||
desktop-app::external_crash_reports
|
||||
desktop-app::external_auto_updates
|
||||
desktop-app::external_openssl
|
||||
desktop-app::external_openal
|
||||
)
|
||||
target_include_directories(Telegram
|
||||
PRIVATE
|
||||
${WAYLAND_CLIENT_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LINUX AND DESKTOP_APP_USE_PACKAGED AND Qt5WaylandClient_VERSION VERSION_LESS 5.13.0)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client)
|
||||
|
||||
target_include_directories(Telegram
|
||||
PRIVATE
|
||||
${WAYLAND_CLIENT_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LINUX)
|
||||
if (DESKTOP_APP_USE_PACKAGED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(XCB_SCREENSAVER REQUIRED IMPORTED_TARGET xcb-screensaver)
|
||||
@@ -155,34 +151,38 @@ if (LINUX)
|
||||
target_link_static_libraries(Telegram PRIVATE xcb-screensaver)
|
||||
target_link_libraries(Telegram PRIVATE xcb)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (LINUX AND NOT TDESKTOP_DISABLE_GTK_INTEGRATION)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
target_compile_options(Telegram PRIVATE -Wno-register)
|
||||
pkg_check_modules(GLIB2 REQUIRED IMPORTED_TARGET glib-2.0)
|
||||
pkg_check_modules(GOBJECT REQUIRED IMPORTED_TARGET gobject-2.0)
|
||||
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
|
||||
|
||||
if (DESKTOP_APP_USE_PACKAGED AND NOT DESKTOP_APP_USE_PACKAGED_LAZY)
|
||||
pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11)
|
||||
pkg_check_modules(GOBJECT2 REQUIRED IMPORTED_TARGET gobject-2.0)
|
||||
pkg_check_modules(GLIB2 REQUIRED IMPORTED_TARGET glib-2.0)
|
||||
pkg_check_modules(GTK3 REQUIRED IMPORTED_TARGET gtk+-3.0)
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
PkgConfig::GLIB2
|
||||
PkgConfig::GOBJECT
|
||||
PkgConfig::GIO
|
||||
)
|
||||
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
PkgConfig::X11
|
||||
PkgConfig::GOBJECT2
|
||||
PkgConfig::GLIB2
|
||||
PkgConfig::GTK3
|
||||
)
|
||||
else()
|
||||
pkg_search_module(GTK REQUIRED gtk+-2.0 gtk+-3.0)
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
X11
|
||||
gobject-2.0
|
||||
glib-2.0
|
||||
)
|
||||
target_include_directories(Telegram PRIVATE ${GTK_INCLUDE_DIRS})
|
||||
target_compile_definitions(Telegram PRIVATE G_LOG_DOMAIN="Telegram")
|
||||
|
||||
if (NOT TDESKTOP_DISABLE_GTK_INTEGRATION)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
if (DESKTOP_APP_USE_PACKAGED AND NOT DESKTOP_APP_USE_PACKAGED_LAZY)
|
||||
pkg_check_modules(GTK3 REQUIRED IMPORTED_TARGET gtk+-3.0)
|
||||
pkg_check_modules(X11 REQUIRED IMPORTED_TARGET x11)
|
||||
|
||||
target_link_libraries(Telegram
|
||||
PRIVATE
|
||||
PkgConfig::GTK3
|
||||
PkgConfig::X11
|
||||
)
|
||||
else()
|
||||
pkg_search_module(GTK REQUIRED gtk+-2.0 gtk+-3.0)
|
||||
target_include_directories(Telegram PRIVATE ${GTK_INCLUDE_DIRS})
|
||||
target_link_libraries(Telegram PRIVATE X11)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -191,6 +191,8 @@ nice_target_sources(Telegram ${src_loc}
|
||||
PRIVATE
|
||||
${style_files}
|
||||
|
||||
api/api_authorizations.cpp
|
||||
api/api_authorizations.h
|
||||
api/api_bot.cpp
|
||||
api/api_bot.h
|
||||
api/api_chat_filters.cpp
|
||||
@@ -218,6 +220,8 @@ PRIVATE
|
||||
api/api_single_message_search.h
|
||||
api/api_text_entities.cpp
|
||||
api/api_text_entities.h
|
||||
api/api_toggling_media.cpp
|
||||
api/api_toggling_media.h
|
||||
api/api_updates.cpp
|
||||
api/api_updates.h
|
||||
boxes/filters/edit_filter_box.cpp
|
||||
@@ -467,6 +471,8 @@ PRIVATE
|
||||
data/data_poll.h
|
||||
data/data_pts_waiter.cpp
|
||||
data/data_pts_waiter.h
|
||||
data/data_replies_list.cpp
|
||||
data/data_replies_list.h
|
||||
data/data_reply_preview.cpp
|
||||
data/data_reply_preview.h
|
||||
data/data_search_controller.cpp
|
||||
@@ -588,10 +594,14 @@ PRIVATE
|
||||
history/view/history_view_message.cpp
|
||||
history/view/history_view_message.h
|
||||
history/view/history_view_object.h
|
||||
history/view/history_view_replies_section.cpp
|
||||
history/view/history_view_replies_section.h
|
||||
history/view/history_view_schedule_box.cpp
|
||||
history/view/history_view_schedule_box.h
|
||||
history/view/history_view_scheduled_section.cpp
|
||||
history/view/history_view_scheduled_section.h
|
||||
history/view/history_view_send_action.cpp
|
||||
history/view/history_view_send_action.h
|
||||
history/view/history_view_service_message.cpp
|
||||
history/view/history_view_service_message.h
|
||||
history/view/history_view_top_bar_widget.cpp
|
||||
@@ -711,23 +721,12 @@ PRIVATE
|
||||
intro/intro_widget.h
|
||||
lang/lang_cloud_manager.cpp
|
||||
lang/lang_cloud_manager.h
|
||||
lang/lang_file_parser.cpp
|
||||
lang/lang_file_parser.h
|
||||
lang/lang_hardcoded.h
|
||||
lang/lang_instance.cpp
|
||||
lang/lang_instance.h
|
||||
lang/lang_keys.cpp
|
||||
lang/lang_keys.h
|
||||
lang/lang_numbers_animation.cpp
|
||||
lang/lang_numbers_animation.h
|
||||
lang/lang_tag.cpp
|
||||
lang/lang_tag.h
|
||||
lang/lang_text_entity.cpp
|
||||
lang/lang_text_entity.h
|
||||
lang/lang_translator.cpp
|
||||
lang/lang_translator.h
|
||||
lang/lang_values.cpp
|
||||
lang/lang_values.h
|
||||
main/main_account.cpp
|
||||
main/main_account.h
|
||||
main/main_app_config.cpp
|
||||
@@ -1141,6 +1140,7 @@ PRIVATE
|
||||
qt_static_plugins.cpp
|
||||
settings.cpp
|
||||
settings.h
|
||||
stdafx.h
|
||||
)
|
||||
|
||||
if (NOT LINUX)
|
||||
@@ -1230,18 +1230,6 @@ elseif (APPLE)
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
elseif (LINUX)
|
||||
if (NOT TDESKTOP_DISABLE_GTK_INTEGRATION)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
pkg_search_module(GTK REQUIRED gtk+-2.0 gtk+-3.0)
|
||||
target_include_directories(Telegram PRIVATE ${GTK_INCLUDE_DIRS})
|
||||
|
||||
if (DESKTOP_APP_USE_PACKAGED)
|
||||
find_library(X11_LIBRARY X11)
|
||||
target_link_libraries(Telegram PRIVATE ${X11_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (build_macstore)
|
||||
|
||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 12 KiB |
BIN
Telegram/Resources/icons/fast_comments.png
Normal file
|
After Width: | Height: | Size: 422 B |
BIN
Telegram/Resources/icons/fast_comments@2x.png
Normal file
|
After Width: | Height: | Size: 767 B |
BIN
Telegram/Resources/icons/fast_comments@3x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 330 B After Width: | Height: | Size: 414 B |
|
Before Width: | Height: | Size: 661 B After Width: | Height: | Size: 764 B |
|
Before Width: | Height: | Size: 696 B After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/fast_to_original.png
Normal file
|
After Width: | Height: | Size: 242 B |
BIN
Telegram/Resources/icons/fast_to_original@2x.png
Normal file
|
After Width: | Height: | Size: 346 B |
BIN
Telegram/Resources/icons/fast_to_original@3x.png
Normal file
|
After Width: | Height: | Size: 473 B |
BIN
Telegram/Resources/icons/history_comments.png
Normal file
|
After Width: | Height: | Size: 609 B |
BIN
Telegram/Resources/icons/history_comments@2x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/history_comments@3x.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Telegram/Resources/icons/history_comments_open.png
Normal file
|
After Width: | Height: | Size: 194 B |
BIN
Telegram/Resources/icons/history_comments_open@2x.png
Normal file
|
After Width: | Height: | Size: 312 B |
BIN
Telegram/Resources/icons/history_comments_open@3x.png
Normal file
|
After Width: | Height: | Size: 393 B |
BIN
Telegram/Resources/icons/history_replies.png
Normal file
|
After Width: | Height: | Size: 315 B |
BIN
Telegram/Resources/icons/history_replies@2x.png
Normal file
|
After Width: | Height: | Size: 553 B |
BIN
Telegram/Resources/icons/history_replies@3x.png
Normal file
|
After Width: | Height: | Size: 573 B |
BIN
Telegram/Resources/icons/replies_userpic.png
Normal file
|
After Width: | Height: | Size: 441 B |
BIN
Telegram/Resources/icons/replies_userpic@2x.png
Normal file
|
After Width: | Height: | Size: 909 B |
BIN
Telegram/Resources/icons/replies_userpic@3x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
@@ -370,6 +370,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_adaptive_wide" = "Adaptive layout for wide screens";
|
||||
|
||||
"lng_settings_section_call_settings" = "Calls Settings";
|
||||
"lng_settings_call_camera" = "Camera";
|
||||
"lng_settings_call_section_output" = "Speakers and headphones";
|
||||
"lng_settings_call_section_input" = "Microphone";
|
||||
"lng_settings_call_input_device" = "Input device";
|
||||
@@ -380,7 +381,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_call_stop_mic_test" = "Stop test";
|
||||
"lng_settings_call_section_other" = "Other settings";
|
||||
"lng_settings_call_open_system_prefs" = "Open system sound preferences";
|
||||
"lng_settings_call_device_default" = "Default";
|
||||
"lng_settings_call_device_default" = "Same as the System";
|
||||
"lng_settings_call_audio_ducking" = "Mute other sounds during calls";
|
||||
|
||||
"lng_settings_language" = "Language";
|
||||
@@ -811,6 +812,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_profile_set_group_photo" = "Set Photo";
|
||||
"lng_profile_add_participant" = "Add Members";
|
||||
"lng_profile_view_channel" = "View Channel";
|
||||
"lng_profile_view_discussion" = "View discussion";
|
||||
"lng_profile_join_channel" = "Join Channel";
|
||||
"lng_profile_join_group" = "Join Group";
|
||||
"lng_profile_delete_and_exit" = "Leave";
|
||||
@@ -1219,6 +1221,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_recent_stickers" = "Frequently used";
|
||||
"lng_faved_stickers_add" = "Add to Favorites";
|
||||
"lng_faved_stickers_remove" = "Remove from Favorites";
|
||||
"lng_recent_stickers_remove" = "Remove from Recent";
|
||||
"lng_group_stickers" = "Group stickers";
|
||||
"lng_group_stickers_description" = "You can choose a sticker set which will be available for every member while in the group chat.";
|
||||
"lng_group_stickers_add" = "Choose sticker set";
|
||||
@@ -1317,6 +1320,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_message_ph" = "Write a message...";
|
||||
"lng_broadcast_ph" = "Broadcast a message...";
|
||||
"lng_broadcast_silent_ph" = "Silent broadcast...";
|
||||
"lng_send_anonymous_ph" = "Send anonymously...";
|
||||
"lng_record_cancel" = "Release outside this field to cancel";
|
||||
"lng_will_be_notified" = "Members will be notified when you post";
|
||||
"lng_wont_be_notified" = "Members will not be notified when you post";
|
||||
@@ -1328,7 +1332,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_restart_button" = "Restart";
|
||||
"lng_channel_mute" = "Mute";
|
||||
"lng_channel_unmute" = "Unmute";
|
||||
"lng_channel_discuss" = "Discuss";
|
||||
"lng_saved_messages" = "Saved Messages";
|
||||
"lng_saved_short" = "Save";
|
||||
"lng_saved_forward_here" = "Forward messages here for quick access";
|
||||
@@ -1342,6 +1345,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_scheduled_send_now_many#one" = "Send {count} message now?";
|
||||
"lng_scheduled_send_now_many#other" = "Send {count} messages now?";
|
||||
|
||||
"lng_replies_view#one" = "View {count} Reply";
|
||||
"lng_replies_view#other" = "View {count} Replies";
|
||||
"lng_replies_view_thread" = "View Thread";
|
||||
"lng_replies_header#one" = "{count} reply";
|
||||
"lng_replies_header#other" = "{count} replies";
|
||||
"lng_replies_header_none" = "Replies";
|
||||
"lng_comments_header#one" = "{count} comment";
|
||||
"lng_comments_header#other" = "{count} comments";
|
||||
"lng_comments_header_none" = "Comments";
|
||||
"lng_comments_open_count#one" = "{count} comment";
|
||||
"lng_comments_open_count#other" = "{count} comments";
|
||||
"lng_comments_open_none" = "Leave a comment";
|
||||
"lng_replies_view_original" = "View in chat";
|
||||
"lng_replies_messages" = "Replies";
|
||||
"lng_replies_discussion_started" = "Discussion started";
|
||||
"lng_replies_no_comments" = "No comments here yet...";
|
||||
|
||||
"lng_archived_name" = "Archived chats";
|
||||
"lng_archived_add" = "Archive";
|
||||
"lng_archived_remove" = "Unarchive";
|
||||
@@ -1437,6 +1457,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_unpin_from_top" = "Unpin from top";
|
||||
"lng_context_mark_unread" = "Mark as unread";
|
||||
"lng_context_mark_read" = "Mark as read";
|
||||
"lng_context_mark_read_sure" = "Are you sure you want to mark all chats from this folder as read?";
|
||||
"lng_context_mark_read_all" = "Mark all chats as read";
|
||||
"lng_context_mark_read_all_sure" = "Are you sure you want to mark all chats as read?";
|
||||
"lng_context_archive_expand" = "Expand";
|
||||
"lng_context_archive_collapse" = "Collapse";
|
||||
"lng_context_archive_to_menu" = "Move to main menu";
|
||||
@@ -1724,6 +1747,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_call_error_not_available" = "Sorry, {user} doesn't accept calls.";
|
||||
"lng_call_error_outdated" = "{user}'s app does not support calls. They need to update their app before you can call them.";
|
||||
"lng_call_error_no_camera" = "No camera could be found. Please make sure that your camera is connected to the computer.";
|
||||
"lng_call_error_camera_not_started" = "You can switch to video call once you're connected.";
|
||||
"lng_call_error_camera_outdated" = "{user}'s app does not support video calls. They need to update their app.";
|
||||
"lng_call_error_audio_io" = "There seems to be a problem with audio playback on your computer. Please make sure that your computer's speakers and microphone are working and try again.";
|
||||
|
||||
"lng_call_bar_hangup" = "End call";
|
||||
@@ -1816,6 +1842,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_rights_group_invite" = "Add users";
|
||||
"lng_rights_group_pin" = "Pin messages";
|
||||
"lng_rights_group_delete" = "Delete messages";
|
||||
"lng_rights_group_anonymous" = "Remain Anonymous";
|
||||
"lng_rights_add_admins" = "Add new admins";
|
||||
"lng_rights_chat_read" = "Read messages";
|
||||
"lng_rights_chat_send_text" = "Send messages";
|
||||
@@ -1848,6 +1875,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_rights_transfer_done_group" = "{user} is now the owner of the group.";
|
||||
"lng_rights_transfer_done_channel" = "{user} is now the owner of the channel.";
|
||||
|
||||
"lng_bots_password_confirm_check_about" = "You can finish this action only if you have:";
|
||||
"lng_bots_password_confirm_title" = "Two-step verification";
|
||||
"lng_bots_password_confirm_description" = "Please enter your password to confirm the action.";
|
||||
|
||||
"lng_restricted_send_message" = "The admins of this group restricted you from writing here.";
|
||||
"lng_restricted_send_media" = "The admins of this group restricted you from posting media content here.";
|
||||
"lng_restricted_send_stickers" = "The admins of this group restricted you from posting stickers here.";
|
||||
|
||||
@@ -206,7 +206,7 @@
|
||||
53;CU;Cuba;53 XXXX XXXX;10;
|
||||
52;MX;Mexico;
|
||||
51;PE;Peru;51 XXX XXX XXX;11;
|
||||
49;DE;Germany;49 XXX XXXXXXXX;13;
|
||||
49;DE;Germany;
|
||||
48;PL;Poland;48 XXX XXX XXX;11;
|
||||
47;NO;Norway;47 XXXX XXXX;10;
|
||||
46;SE;Sweden;46 XX XXX XXXX;11;
|
||||
|
||||
@@ -128,7 +128,7 @@ channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.
|
||||
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
|
||||
|
||||
chatFull#1b7c9db3 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
|
||||
channelFull#f0e6672a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int = ChatFull;
|
||||
channelFull#f0e6672a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int = ChatFull;
|
||||
|
||||
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
|
||||
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
|
||||
@@ -141,8 +141,8 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
|
||||
chatPhoto#d20b9f3c flags:# has_video:flags.0?true photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto;
|
||||
|
||||
messageEmpty#83e5de54 id:int = Message;
|
||||
message#452c0e65 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
|
||||
messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
|
||||
message#58ae39c9 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
|
||||
messageService#286fa604 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction = Message;
|
||||
|
||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||
messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
||||
@@ -192,6 +192,7 @@ photoSizeEmpty#e17e23c type:string = PhotoSize;
|
||||
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
|
||||
photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
|
||||
photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize;
|
||||
photoSizeProgressive#5aa86a51 type:string location:FileLocation w:int h:int sizes:Vector<int> = PhotoSize;
|
||||
|
||||
geoPointEmpty#1117dd5f = GeoPoint;
|
||||
geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint;
|
||||
@@ -231,8 +232,6 @@ contact#f911c994 user_id:int mutual:Bool = Contact;
|
||||
|
||||
importedContact#d0028438 user_id:int client_id:long = ImportedContact;
|
||||
|
||||
contactBlocked#561bc879 user_id:int date:int = ContactBlocked;
|
||||
|
||||
contactStatus#d3680c61 user_id:int status:UserStatus = ContactStatus;
|
||||
|
||||
contacts.contactsNotModified#b74ba9d2 = contacts.Contacts;
|
||||
@@ -240,8 +239,8 @@ contacts.contacts#eae87e42 contacts:Vector<Contact> saved_count:int users:Vector
|
||||
|
||||
contacts.importedContacts#77d01c3b imported:Vector<ImportedContact> popular_invites:Vector<PopularContact> retry_contacts:Vector<long> users:Vector<User> = contacts.ImportedContacts;
|
||||
|
||||
contacts.blocked#1c138d15 blocked:Vector<ContactBlocked> users:Vector<User> = contacts.Blocked;
|
||||
contacts.blockedSlice#900802a1 count:int blocked:Vector<ContactBlocked> users:Vector<User> = contacts.Blocked;
|
||||
contacts.blocked#ade1591 blocked:Vector<PeerBlocked> chats:Vector<Chat> users:Vector<User> = contacts.Blocked;
|
||||
contacts.blockedSlice#e1664194 count:int blocked:Vector<PeerBlocked> chats:Vector<Chat> users:Vector<User> = contacts.Blocked;
|
||||
|
||||
messages.dialogs#15ba6c40 dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
|
||||
messages.dialogsSlice#71e094f3 count:int dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
|
||||
@@ -292,7 +291,6 @@ updateEncryptedMessagesRead#38fe25b7 chat_id:int max_date:int date:int = Update;
|
||||
updateChatParticipantAdd#ea4b0e5c chat_id:int user_id:int inviter_id:int date:int version:int = Update;
|
||||
updateChatParticipantDelete#6e5f8c22 chat_id:int user_id:int version:int = Update;
|
||||
updateDcOptions#8e5e9873 dc_options:Vector<DcOption> = Update;
|
||||
updateUserBlocked#80ece81a user_id:int blocked:Bool = Update;
|
||||
updateNotifySettings#bec268ef peer:NotifyPeer notify_settings:PeerNotifySettings = Update;
|
||||
updateServiceNotification#ebe46819 flags:# popup:flags.0?true inbox_date:flags.1?int type:string message:string media:MessageMedia entities:Vector<MessageEntity> = Update;
|
||||
updatePrivacy#ee3b272a key:PrivacyKey rules:Vector<PrivacyRule> = Update;
|
||||
@@ -358,6 +356,11 @@ updateDialogFilterOrder#a5d72105 order:Vector<int> = Update;
|
||||
updateDialogFilters#3504914f = Update;
|
||||
updatePhoneCallSignalingData#2661bf09 phone_call_id:long data:bytes = Update;
|
||||
updateChannelParticipant#65d2b464 flags:# channel_id:int date:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant qts:int = Update;
|
||||
updateChannelMessageForwards#6e8a84df channel_id:int id:int forwards:int = Update;
|
||||
updateReadChannelDiscussionInbox#1cc7de54 flags:# channel_id:int top_msg_id:int read_max_id:int broadcast_id:flags.0?int broadcast_post:flags.0?int = Update;
|
||||
updateReadChannelDiscussionOutbox#4638a26c channel_id:int top_msg_id:int read_max_id:int = Update;
|
||||
updatePeerBlocked#246a4b22 peer_id:Peer blocked:Bool = Update;
|
||||
updateChannelUserTyping#ff2abe9f flags:# channel_id:int top_msg_id:flags.0?int user_id:int action:SendMessageAction = Update;
|
||||
|
||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||
|
||||
@@ -367,8 +370,8 @@ updates.differenceSlice#a8fb1981 new_messages:Vector<Message> new_encrypted_mess
|
||||
updates.differenceTooLong#4afe8f6d pts:int = updates.Difference;
|
||||
|
||||
updatesTooLong#e317af7e = Updates;
|
||||
updateShortMessage#914fbf11 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int entities:flags.7?Vector<MessageEntity> = Updates;
|
||||
updateShortChatMessage#16812688 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int entities:flags.7?Vector<MessageEntity> = Updates;
|
||||
updateShortMessage#2296d2c8 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector<MessageEntity> = Updates;
|
||||
updateShortChatMessage#402d5dbb flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to:flags.3?MessageReplyHeader entities:flags.7?Vector<MessageEntity> = Updates;
|
||||
updateShort#78d4dec1 update:Update date:int = Updates;
|
||||
updatesCombined#725b04c3 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq_start:int seq:int = Updates;
|
||||
updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates;
|
||||
@@ -547,7 +550,7 @@ botInfo#98e81d3a user_id:int description:string commands:Vector<BotCommand> = Bo
|
||||
|
||||
keyboardButton#a2fa4880 text:string = KeyboardButton;
|
||||
keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton;
|
||||
keyboardButtonCallback#683a5e46 text:string data:bytes = KeyboardButton;
|
||||
keyboardButtonCallback#35bbdb6b flags:# requires_password:flags.0?true text:string data:bytes = KeyboardButton;
|
||||
keyboardButtonRequestPhone#b16a6c29 text:string = KeyboardButton;
|
||||
keyboardButtonRequestGeoLocation#fc796b3f text:string = KeyboardButton;
|
||||
keyboardButtonSwitchInline#568a748 flags:# same_peer:flags.0?true text:string query:string = KeyboardButton;
|
||||
@@ -601,7 +604,7 @@ channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:
|
||||
|
||||
channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant;
|
||||
channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant;
|
||||
channelParticipantCreator#808d15a4 flags:# user_id:int rank:flags.0?string = ChannelParticipant;
|
||||
channelParticipantCreator#447dca4b flags:# user_id:int admin_rights:ChatAdminRights rank:flags.0?string = ChannelParticipant;
|
||||
channelParticipantAdmin#ccbebbaf flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant;
|
||||
channelParticipantBanned#1c0facaf flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChatBannedRights = ChannelParticipant;
|
||||
|
||||
@@ -648,7 +651,7 @@ messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_off
|
||||
|
||||
exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink;
|
||||
|
||||
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;
|
||||
messageFwdHeader#5f777dce flags:# from_id:flags.0?Peer from_name:flags.5?string date: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;
|
||||
@@ -1029,7 +1032,7 @@ chatOnlines#f041e250 onlines:int = ChatOnlines;
|
||||
|
||||
statsURL#47a971e0 url:string = StatsURL;
|
||||
|
||||
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true = ChatAdminRights;
|
||||
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true anonymous:flags.10?true = ChatAdminRights;
|
||||
|
||||
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights;
|
||||
|
||||
@@ -1150,6 +1153,27 @@ stats.megagroupStats#ef7ff916 period:StatsDateRangeDays members:StatsAbsValueAnd
|
||||
|
||||
globalPrivacySettings#bea2f424 flags:# archive_and_mute_new_noncontact_peers:flags.0?Bool = GlobalPrivacySettings;
|
||||
|
||||
help.countryCode#4203c5ef flags:# country_code:string prefixes:flags.0?Vector<string> patterns:flags.1?Vector<string> = help.CountryCode;
|
||||
|
||||
help.country#c3878e23 flags:# hidden:flags.0?true iso2:string default_name:string name:flags.1?string country_codes:Vector<help.CountryCode> = help.Country;
|
||||
|
||||
help.countriesListNotModified#93cc1f32 = help.CountriesList;
|
||||
help.countriesList#87d0759e countries:Vector<help.Country> hash:int = help.CountriesList;
|
||||
|
||||
messageViews#455b853d flags:# views:flags.0?int forwards:flags.1?int replies:flags.2?MessageReplies = MessageViews;
|
||||
|
||||
messages.messageViews#b6c4f543 views:Vector<MessageViews> chats:Vector<Chat> users:Vector<User> = messages.MessageViews;
|
||||
|
||||
stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats;
|
||||
|
||||
messages.discussionMessage#f5dd8f9d flags:# messages:Vector<Message> max_id:flags.0?int read_inbox_max_id:flags.1?int read_outbox_max_id:flags.2?int chats:Vector<Chat> users:Vector<User> = messages.DiscussionMessage;
|
||||
|
||||
messageReplyHeader#a6d57763 flags:# reply_to_msg_id:int reply_to_peer_id:flags.0?Peer reply_to_top_id:flags.1?int = MessageReplyHeader;
|
||||
|
||||
messageReplies#4128faac flags:# comments:flags.0?true replies:int replies_pts:int recent_repliers:flags.1?Vector<Peer> channel_id:flags.0?int max_id:flags.2?int read_max_id:flags.3?int = MessageReplies;
|
||||
|
||||
peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@@ -1257,8 +1281,8 @@ contacts.getContacts#c023849f hash:int = contacts.Contacts;
|
||||
contacts.importContacts#2c800be5 contacts:Vector<InputContact> = contacts.ImportedContacts;
|
||||
contacts.deleteContacts#96a0e00 id:Vector<InputUser> = Updates;
|
||||
contacts.deleteByPhones#1013fd9e phones:Vector<string> = Bool;
|
||||
contacts.block#332b49fc id:InputUser = Bool;
|
||||
contacts.unblock#e54100bd id:InputUser = Bool;
|
||||
contacts.block#68cc1411 id:InputPeer = Bool;
|
||||
contacts.unblock#bea65d50 id:InputPeer = Bool;
|
||||
contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked;
|
||||
contacts.search#11f812d8 q:string limit:int = contacts.Found;
|
||||
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
|
||||
@@ -1270,19 +1294,20 @@ contacts.toggleTopPeers#8514bdda enabled:Bool = Bool;
|
||||
contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string = Updates;
|
||||
contacts.acceptContact#f831a20f id:InputUser = Updates;
|
||||
contacts.getLocated#d348bc44 flags:# background:flags.1?true geo_point:InputGeoPoint self_expires:flags.0?int = Updates;
|
||||
contacts.blockFromReplies#29a8962c flags:# delete_message:flags.0?true delete_history:flags.1?true report_spam:flags.2?true msg_id:int = Updates;
|
||||
|
||||
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
|
||||
messages.getDialogs#a0ee3b73 flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs;
|
||||
messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
|
||||
messages.search#8614ef68 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
|
||||
messages.search#4e17810b flags:# peer:InputPeer q:string from_id:flags.0?InputUser top_msg_id:flags.1?int filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
|
||||
messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages;
|
||||
messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int = messages.AffectedHistory;
|
||||
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
|
||||
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
||||
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
|
||||
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
|
||||
messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
|
||||
messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
|
||||
messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates;
|
||||
messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates;
|
||||
messages.reportSpam#cf1592db peer:InputPeer = Bool;
|
||||
messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings;
|
||||
messages.report#bd82b658 peer:InputPeer id:Vector<int> reason:ReportReason = Bool;
|
||||
@@ -1299,8 +1324,8 @@ messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerp
|
||||
messages.discardEncryption#edd923c5 chat_id:int = Bool;
|
||||
messages.setEncryptedTyping#791451ed peer:InputEncryptedChat typing:Bool = Bool;
|
||||
messages.readEncryptedHistory#7f4b690a peer:InputEncryptedChat max_date:int = Bool;
|
||||
messages.sendEncrypted#a9776773 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
|
||||
messages.sendEncryptedFile#9a901b66 peer:InputEncryptedChat random_id:long data:bytes file:InputEncryptedFile = messages.SentEncryptedMessage;
|
||||
messages.sendEncrypted#44fa7a15 flags:# silent:flags.0?true peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
|
||||
messages.sendEncryptedFile#5559481d flags:# silent:flags.0?true peer:InputEncryptedChat random_id:long data:bytes file:InputEncryptedFile = messages.SentEncryptedMessage;
|
||||
messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
|
||||
messages.receivedQueue#55a5bb66 max_qts:int = Vector<long>;
|
||||
messages.reportEncryptedSpam#4b0c8c0f peer:InputEncryptedChat = Bool;
|
||||
@@ -1315,10 +1340,10 @@ messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet
|
||||
messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult;
|
||||
messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;
|
||||
messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates;
|
||||
messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector<int> increment:Bool = Vector<int>;
|
||||
messages.getMessagesViews#5784d3e1 peer:InputPeer id:Vector<int> increment:Bool = messages.MessageViews;
|
||||
messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool;
|
||||
messages.migrateChat#15a3b8e3 chat_id:int = Updates;
|
||||
messages.searchGlobal#bf7225a4 flags:# folder_id:flags.0?int q:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
|
||||
messages.searchGlobal#4bc6589a flags:# folder_id:flags.0?int q:string filter:MessagesFilter min_date:int max_date:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
|
||||
messages.reorderStickerSets#78337739 flags:# masks:flags.0?true order:Vector<long> = Bool;
|
||||
messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document;
|
||||
messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
|
||||
@@ -1329,7 +1354,7 @@ messages.sendInlineBotResult#220815b0 flags:# silent:flags.5?true background:fla
|
||||
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
|
||||
messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
|
||||
messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer;
|
||||
messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
|
||||
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
|
||||
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
|
||||
messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
@@ -1394,6 +1419,9 @@ 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;
|
||||
messages.getReplies#24b581ba peer:InputPeer msg_id:int offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
|
||||
messages.getDiscussionMessage#446972fd peer:InputPeer msg_id:int = messages.DiscussionMessage;
|
||||
messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Bool;
|
||||
|
||||
updates.getState#edd4882a = updates.State;
|
||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||
@@ -1434,6 +1462,7 @@ help.editUserInfo#66b91b70 user_id:InputUser message:string entities:Vector<Mess
|
||||
help.getPromoData#c0977421 = help.PromoData;
|
||||
help.hidePromoData#1e251c95 peer:InputPeer = Bool;
|
||||
help.dismissSuggestion#77fa99f suggestion:string = Bool;
|
||||
help.getCountriesList#735787a8 lang_code:string hash:int = help.CountriesList;
|
||||
|
||||
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
|
||||
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
|
||||
@@ -1454,7 +1483,7 @@ channels.joinChannel#24b524c5 channel:InputChannel = Updates;
|
||||
channels.leaveChannel#f836aa95 channel:InputChannel = Updates;
|
||||
channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> = Updates;
|
||||
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
|
||||
channels.exportMessageLink#ceb77163 channel:InputChannel id:int grouped:Bool = ExportedMessageLink;
|
||||
channels.exportMessageLink#e63fadeb flags:# grouped:flags.0?true thread:flags.1?true channel:InputChannel id:int = ExportedMessageLink;
|
||||
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
|
||||
channels.getAdminedPublicChannels#f8b036af flags:# by_location:flags.0?true check_limit:flags.1?true = messages.Chats;
|
||||
channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_rights:ChatBannedRights = Updates;
|
||||
@@ -1511,5 +1540,7 @@ 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;
|
||||
stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel = stats.MegagroupStats;
|
||||
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
|
||||
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
|
||||
|
||||
// LAYER 117
|
||||
// LAYER 119
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="2.3.0.0" />
|
||||
Version="2.4.1.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,3,0,0
|
||||
PRODUCTVERSION 2,3,0,0
|
||||
FILEVERSION 2,4,1,0
|
||||
PRODUCTVERSION 2,4,1,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "2.3.0.0"
|
||||
VALUE "FileVersion", "2.4.1.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.3.0.0"
|
||||
VALUE "ProductVersion", "2.4.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,3,0,0
|
||||
PRODUCTVERSION 2,3,0,0
|
||||
FILEVERSION 2,4,1,0
|
||||
PRODUCTVERSION 2,4,1,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "2.3.0.0"
|
||||
VALUE "FileVersion", "2.4.1.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2020"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.3.0.0"
|
||||
VALUE "ProductVersion", "2.4.1.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
166
Telegram/SourceFiles/api/api_authorizations.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_authorizations.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "core/changelogs.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
constexpr auto TestApiId = 17349;
|
||||
constexpr auto DesktopApiId = 2040;
|
||||
|
||||
Authorizations::Entry ParseEntry(const MTPDauthorization &data) {
|
||||
auto result = Authorizations::Entry();
|
||||
|
||||
result.hash = data.is_current() ? 0 : data.vhash().v;
|
||||
result.incomplete = data.is_password_pending();
|
||||
|
||||
const auto apiId = data.vapi_id().v;
|
||||
const auto isTest = (apiId == TestApiId);
|
||||
const auto isDesktop = (apiId == DesktopApiId) || isTest;
|
||||
|
||||
const auto appName = isDesktop
|
||||
? QString("Telegram Desktop%1").arg(isTest ? " (GitHub)" : QString())
|
||||
: qs(data.vapp_name());// +qsl(" for ") + qs(d.vplatform());
|
||||
const auto appVer = [&] {
|
||||
const auto version = qs(data.vapp_version());
|
||||
if (isDesktop) {
|
||||
const auto verInt = version.toInt();
|
||||
if (version == QString::number(verInt)) {
|
||||
return Core::FormatVersionDisplay(verInt);
|
||||
}
|
||||
} else {
|
||||
if (const auto index = version.indexOf('('); index >= 0) {
|
||||
return version.mid(index);
|
||||
}
|
||||
}
|
||||
return version;
|
||||
}();
|
||||
|
||||
result.name = QString("%1%2")
|
||||
.arg(appName)
|
||||
.arg(appVer.isEmpty() ? QString() : (' ' + appVer));
|
||||
|
||||
const auto country = qs(data.vcountry());
|
||||
const auto platform = qs(data.vplatform());
|
||||
//const auto &countries = countriesByISO2();
|
||||
//const auto j = countries.constFind(country);
|
||||
//if (j != countries.cend()) {
|
||||
// country = QString::fromUtf8(j.value()->name);
|
||||
//}
|
||||
|
||||
result.activeTime = data.vdate_active().v
|
||||
? data.vdate_active().v
|
||||
: data.vdate_created().v;
|
||||
result.info = QString("%1, %2%3")
|
||||
.arg(qs(data.vdevice_model()))
|
||||
.arg(platform.isEmpty() ? QString() : platform + ' ')
|
||||
.arg(qs(data.vsystem_version()));
|
||||
result.ip = qs(data.vip())
|
||||
+ (country.isEmpty()
|
||||
? QString()
|
||||
: QString::fromUtf8(" \xe2\x80\x93 ") + country);
|
||||
if (!result.hash) {
|
||||
result.active = tr::lng_status_online(tr::now);
|
||||
} else {
|
||||
const auto now = QDateTime::currentDateTime();
|
||||
const auto lastTime = base::unixtime::parse(result.activeTime);
|
||||
const auto nowDate = now.date();
|
||||
const auto lastDate = lastTime.date();
|
||||
if (lastDate == nowDate) {
|
||||
result.active = lastTime.toString(cTimeFormat());
|
||||
} else if (lastDate.year() == nowDate.year()
|
||||
&& lastDate.weekNumber() == nowDate.weekNumber()) {
|
||||
result.active = langDayOfWeek(lastDate);
|
||||
} else {
|
||||
result.active = lastDate.toString(qsl("d.MM.yy"));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Authorizations::Authorizations(not_null<ApiWrap*> api)
|
||||
: _api(&api->instance()) {
|
||||
}
|
||||
|
||||
void Authorizations::reload() {
|
||||
if (_requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
_requestId = _api.request(MTPaccount_GetAuthorizations(
|
||||
)).done([=](const MTPaccount_Authorizations &result) {
|
||||
_requestId = 0;
|
||||
_lastReceived = crl::now();
|
||||
result.match([&](const MTPDaccount_authorizations &auths) {
|
||||
_list = (
|
||||
auths.vauthorizations().v
|
||||
) | ranges::view::transform([](const MTPAuthorization &d) {
|
||||
return ParseEntry(d.c_authorization());
|
||||
}) | ranges::to<List>;
|
||||
_listChanges.fire({});
|
||||
});
|
||||
}).fail([=](const RPCError &error) {
|
||||
_requestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
void Authorizations::cancelCurrentRequest() {
|
||||
_api.request(base::take(_requestId)).cancel();
|
||||
}
|
||||
|
||||
void Authorizations::requestTerminate(
|
||||
Fn<void(const MTPBool &result)> &&done,
|
||||
Fn<void(const RPCError &error)> &&fail,
|
||||
std::optional<uint64> hash) {
|
||||
auto request = hash
|
||||
? MTPaccount_ResetAuthorization(MTP_long(*hash))
|
||||
: MTPaccount_ResetAuthorization();
|
||||
_api.request(std::move(request))
|
||||
.done(std::move(done))
|
||||
.fail(std::move(fail))
|
||||
.send();
|
||||
}
|
||||
|
||||
Authorizations::List Authorizations::list() const {
|
||||
return _list;
|
||||
}
|
||||
|
||||
auto Authorizations::listChanges() const
|
||||
-> rpl::producer<Authorizations::List> {
|
||||
return rpl::single(
|
||||
list()
|
||||
) | rpl::then(
|
||||
_listChanges.events() | rpl::map([=] { return list(); }));
|
||||
}
|
||||
|
||||
rpl::producer<int> Authorizations::totalChanges() const {
|
||||
return rpl::single(
|
||||
total()
|
||||
) | rpl::then(
|
||||
_listChanges.events() | rpl::map([=] { return total(); }));
|
||||
}
|
||||
|
||||
int Authorizations::total() const {
|
||||
return ranges::count_if(
|
||||
_list,
|
||||
ranges::not_fn(&Entry::incomplete));
|
||||
}
|
||||
|
||||
crl::time Authorizations::lastReceivedTime() {
|
||||
return _lastReceived;
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
54
Telegram/SourceFiles/api/api_authorizations.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
class ApiWrap;
|
||||
|
||||
namespace Api {
|
||||
|
||||
class Authorizations final {
|
||||
public:
|
||||
explicit Authorizations(not_null<ApiWrap*> api);
|
||||
|
||||
struct Entry {
|
||||
uint64 hash = 0;
|
||||
|
||||
bool incomplete = false;
|
||||
TimeId activeTime = 0;
|
||||
QString name, active, info, ip;
|
||||
};
|
||||
using List = std::vector<Entry>;
|
||||
|
||||
void reload();
|
||||
void cancelCurrentRequest();
|
||||
void requestTerminate(
|
||||
Fn<void(const MTPBool &result)> &&done,
|
||||
Fn<void(const RPCError &error)> &&fail,
|
||||
std::optional<uint64> hash = std::nullopt);
|
||||
|
||||
[[nodiscard]] crl::time lastReceivedTime();
|
||||
|
||||
[[nodiscard]] List list() const;
|
||||
[[nodiscard]] rpl::producer<List> listChanges() const;
|
||||
[[nodiscard]] int total() const;
|
||||
[[nodiscard]] rpl::producer<int> totalChanges() const;
|
||||
|
||||
private:
|
||||
MTP::Sender _api;
|
||||
mtpRequestId _requestId = 0;
|
||||
|
||||
List _list;
|
||||
rpl::event_stream<> _listChanges;
|
||||
|
||||
crl::time _lastReceived = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Api
|
||||
@@ -8,9 +8,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_bot.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "core/core_cloud_password.h"
|
||||
#include "api/api_send_progress.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "boxes/passcode_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_peer.h"
|
||||
@@ -20,10 +23,129 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item_components.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
void SendBotCallbackData(
|
||||
not_null<HistoryItem*> item,
|
||||
int row,
|
||||
int column,
|
||||
std::optional<MTPInputCheckPasswordSRP> password = std::nullopt,
|
||||
Fn<void(const RPCError &)> handleError = nullptr) {
|
||||
if (!IsServerMsgId(item->id)) {
|
||||
return;
|
||||
}
|
||||
const auto history = item->history();
|
||||
const auto session = &history->session();
|
||||
const auto owner = &history->owner();
|
||||
const auto api = &session->api();
|
||||
const auto bot = item->getMessageBot();
|
||||
const auto fullId = item->fullId();
|
||||
const auto getButton = [=] {
|
||||
return HistoryMessageMarkupButton::Get(
|
||||
owner,
|
||||
fullId,
|
||||
row,
|
||||
column);
|
||||
};
|
||||
const auto button = getButton();
|
||||
if (!button || button->requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
using ButtonType = HistoryMessageMarkupButton::Type;
|
||||
const auto isGame = (button->type == ButtonType::Game);
|
||||
|
||||
auto flags = MTPmessages_GetBotCallbackAnswer::Flags(0);
|
||||
QByteArray sendData;
|
||||
if (isGame) {
|
||||
flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_game;
|
||||
} else if (button->type == ButtonType::Callback
|
||||
|| button->type == ButtonType::CallbackWithPassword) {
|
||||
flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_data;
|
||||
sendData = button->data;
|
||||
}
|
||||
const auto withPassword = password.has_value();
|
||||
if (withPassword) {
|
||||
flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_password;
|
||||
}
|
||||
button->requestId = api->request(MTPmessages_GetBotCallbackAnswer(
|
||||
MTP_flags(flags),
|
||||
history->peer->input,
|
||||
MTP_int(item->id),
|
||||
MTP_bytes(sendData),
|
||||
password.value_or(MTP_inputCheckPasswordEmpty())
|
||||
)).done([=](const MTPmessages_BotCallbackAnswer &result) {
|
||||
const auto item = owner->message(fullId);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (const auto button = getButton()) {
|
||||
button->requestId = 0;
|
||||
owner->requestItemRepaint(item);
|
||||
}
|
||||
result.match([&](const MTPDmessages_botCallbackAnswer &data) {
|
||||
if (const auto message = data.vmessage()) {
|
||||
if (data.is_alert()) {
|
||||
Ui::show(Box<InformBox>(qs(*message)));
|
||||
} else {
|
||||
if (withPassword) {
|
||||
Ui::hideLayer();
|
||||
}
|
||||
Ui::Toast::Show(qs(*message));
|
||||
}
|
||||
} else if (const auto url = data.vurl()) {
|
||||
const auto link = qs(*url);
|
||||
if (!isGame) {
|
||||
UrlClickHandler::Open(link);
|
||||
return;
|
||||
}
|
||||
const auto scoreLink = AppendShareGameScoreUrl(
|
||||
session,
|
||||
link,
|
||||
item->fullId());
|
||||
BotGameUrlClickHandler(bot, scoreLink).onClick({});
|
||||
session->sendProgressManager().update(
|
||||
history,
|
||||
Api::SendProgressType::PlayGame);
|
||||
} else if (withPassword) {
|
||||
Ui::hideLayer();
|
||||
}
|
||||
});
|
||||
}).fail([=](const RPCError &error) {
|
||||
const auto item = owner->message(fullId);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
// Show error?
|
||||
if (const auto button = getButton()) {
|
||||
button->requestId = 0;
|
||||
owner->requestItemRepaint(item);
|
||||
}
|
||||
if (handleError) {
|
||||
handleError(error);
|
||||
}
|
||||
}).send();
|
||||
|
||||
session->changes().messageUpdated(
|
||||
item,
|
||||
Data::MessageUpdate::Flag::BotCallbackSent
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SendBotCallbackData(
|
||||
not_null<HistoryItem*> item,
|
||||
int row,
|
||||
int column) {
|
||||
SendBotCallbackData(item, row, column, MTP_inputCheckPasswordEmpty());
|
||||
}
|
||||
|
||||
void SendBotCallbackDataWithPassword(
|
||||
not_null<HistoryItem*> item,
|
||||
int row,
|
||||
int column) {
|
||||
@@ -44,74 +166,63 @@ void SendBotCallbackData(
|
||||
column);
|
||||
};
|
||||
const auto button = getButton();
|
||||
if (!button) {
|
||||
if (!button || button->requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
using ButtonType = HistoryMessageMarkupButton::Type;
|
||||
const auto isGame = (button->type == ButtonType::Game);
|
||||
|
||||
auto flags = MTPmessages_GetBotCallbackAnswer::Flags(0);
|
||||
QByteArray sendData;
|
||||
if (isGame) {
|
||||
flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_game;
|
||||
} else if (button->type == ButtonType::Callback) {
|
||||
flags |= MTPmessages_GetBotCallbackAnswer::Flag::f_data;
|
||||
sendData = button->data;
|
||||
}
|
||||
button->requestId = api->request(MTPmessages_GetBotCallbackAnswer(
|
||||
MTP_flags(flags),
|
||||
history->peer->input,
|
||||
MTP_int(item->id),
|
||||
MTP_bytes(sendData)
|
||||
)).done([=](const MTPmessages_BotCallbackAnswer &result) {
|
||||
const auto item = owner->message(fullId);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
if (const auto button = getButton()) {
|
||||
button->requestId = 0;
|
||||
owner->requestItemRepaint(item);
|
||||
}
|
||||
result.match([&](const MTPDmessages_botCallbackAnswer &data) {
|
||||
if (const auto message = data.vmessage()) {
|
||||
if (data.is_alert()) {
|
||||
Ui::show(Box<InformBox>(qs(*message)));
|
||||
} else {
|
||||
Ui::Toast::Show(qs(*message));
|
||||
api->reloadPasswordState();
|
||||
SendBotCallbackData(item, row, column, MTP_inputCheckPasswordEmpty(), [=](const RPCError &error) {
|
||||
auto box = PrePasswordErrorBox(
|
||||
error,
|
||||
session,
|
||||
tr::lng_bots_password_confirm_check_about(
|
||||
tr::now,
|
||||
Ui::Text::WithEntities));
|
||||
if (box) {
|
||||
Ui::show(std::move(box));
|
||||
} else {
|
||||
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||
button->requestId = -1;
|
||||
api->passwordState(
|
||||
) | rpl::take(
|
||||
1
|
||||
) | rpl::start_with_next([=](const Core::CloudPasswordState &state) mutable {
|
||||
if (lifetime) {
|
||||
base::take(lifetime)->destroy();
|
||||
}
|
||||
} else if (const auto url = data.vurl()) {
|
||||
const auto link = qs(*url);
|
||||
if (!isGame) {
|
||||
UrlClickHandler::Open(link);
|
||||
if (const auto button = getButton()) {
|
||||
if (button->requestId == -1) {
|
||||
button->requestId = 0;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
const auto scoreLink = AppendShareGameScoreUrl(
|
||||
session,
|
||||
link,
|
||||
item->fullId());
|
||||
BotGameUrlClickHandler(bot, scoreLink).onClick({});
|
||||
session->sendProgressManager().update(
|
||||
history,
|
||||
Api::SendProgressType::PlayGame);
|
||||
}
|
||||
});
|
||||
}).fail([=](const RPCError &error) {
|
||||
const auto item = owner->message(fullId);
|
||||
if (!item) {
|
||||
return;
|
||||
const auto box = std::make_shared<QPointer<PasscodeBox>>();
|
||||
auto fields = PasscodeBox::CloudFields::From(state);
|
||||
fields.customTitle = tr::lng_bots_password_confirm_title();
|
||||
fields.customDescription
|
||||
= tr::lng_bots_password_confirm_description(tr::now);
|
||||
fields.customSubmitButton = tr::lng_passcode_submit();
|
||||
fields.customCheckCallback = [=](
|
||||
const Core::CloudPasswordResult &result) {
|
||||
if (const auto button = getButton()) {
|
||||
if (button->requestId) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (const auto item = owner->message(fullId)) {
|
||||
SendBotCallbackData(item, row, column, result.result, [=](const RPCError &error) {
|
||||
if (*box) {
|
||||
(*box)->handleCustomCheckError(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
*box = Ui::show(Box<PasscodeBox>(session, fields));
|
||||
}, *lifetime);
|
||||
}
|
||||
// Show error?
|
||||
if (const auto button = getButton()) {
|
||||
button->requestId = 0;
|
||||
owner->requestItemRepaint(item);
|
||||
}
|
||||
}).send();
|
||||
|
||||
session->changes().messageUpdated(
|
||||
item,
|
||||
Data::MessageUpdate::Flag::BotCallbackSent
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -16,4 +16,9 @@ void SendBotCallbackData(
|
||||
int row,
|
||||
int column);
|
||||
|
||||
void SendBotCallbackDataWithPassword(
|
||||
not_null<HistoryItem*> item,
|
||||
int row,
|
||||
int column);
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Api {
|
||||
namespace {
|
||||
|
||||
constexpr auto kCancelTypingActionTimeout = crl::time(5000);
|
||||
constexpr auto kSetMyActionForMs = 10 * crl::time(1000);
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -27,7 +28,14 @@ SendProgressManager::SendProgressManager(not_null<Main::Session*> session)
|
||||
void SendProgressManager::cancel(
|
||||
not_null<History*> history,
|
||||
SendProgressType type) {
|
||||
const auto i = _requests.find({ history, type });
|
||||
cancel(history, 0, type);
|
||||
}
|
||||
|
||||
void SendProgressManager::cancel(
|
||||
not_null<History*> history,
|
||||
MsgId topMsgId,
|
||||
SendProgressType type) {
|
||||
const auto i = _requests.find(Key{ history, topMsgId, type });
|
||||
if (i != _requests.end()) {
|
||||
_session->api().request(i->second).cancel();
|
||||
_requests.erase(i);
|
||||
@@ -42,29 +50,58 @@ void SendProgressManager::cancelTyping(not_null<History*> history) {
|
||||
void SendProgressManager::update(
|
||||
not_null<History*> history,
|
||||
SendProgressType type,
|
||||
int32 progress) {
|
||||
int progress) {
|
||||
update(history, 0, type, progress);
|
||||
}
|
||||
|
||||
void SendProgressManager::update(
|
||||
not_null<History*> history,
|
||||
MsgId topMsgId,
|
||||
SendProgressType type,
|
||||
int progress) {
|
||||
const auto peer = history->peer;
|
||||
if (peer->isSelf() || (peer->isChannel() && !peer->isMegagroup())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto doing = (progress >= 0);
|
||||
if (history->mySendActionUpdated(type, doing)) {
|
||||
cancel(history, type);
|
||||
const auto key = Key{ history, topMsgId, type };
|
||||
if (updated(key, doing)) {
|
||||
cancel(history, topMsgId, type);
|
||||
if (doing) {
|
||||
send(history, type, progress);
|
||||
send(key, progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SendProgressManager::send(
|
||||
not_null<History*> history,
|
||||
SendProgressType type,
|
||||
int32 progress) {
|
||||
bool SendProgressManager::updated(const Key &key, bool doing) {
|
||||
const auto now = crl::now();
|
||||
const auto i = _updated.find(key);
|
||||
if (doing) {
|
||||
if (i == end(_updated)) {
|
||||
_updated.emplace(key, now + kSetMyActionForMs);
|
||||
} else if (i->second > now + (kSetMyActionForMs / 2)) {
|
||||
return false;
|
||||
} else {
|
||||
i->second = now + kSetMyActionForMs;
|
||||
}
|
||||
} else {
|
||||
if (i == end(_updated)) {
|
||||
return false;
|
||||
} else if (i->second <= now) {
|
||||
return false;
|
||||
} else {
|
||||
_updated.erase(i);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SendProgressManager::send(const Key &key, int progress) {
|
||||
using Type = SendProgressType;
|
||||
const auto action = [&]() -> MTPsendMessageAction {
|
||||
const auto p = MTP_int(progress);
|
||||
switch (type) {
|
||||
switch (key.type) {
|
||||
case Type::Typing: return MTP_sendMessageTypingAction();
|
||||
case Type::RecordVideo: return MTP_sendMessageRecordVideoAction();
|
||||
case Type::UploadVideo: return MTP_sendMessageUploadVideoAction(p);
|
||||
@@ -81,15 +118,19 @@ void SendProgressManager::send(
|
||||
}
|
||||
}();
|
||||
const auto requestId = _session->api().request(MTPmessages_SetTyping(
|
||||
history->peer->input,
|
||||
MTP_flags(key.topMsgId
|
||||
? MTPmessages_SetTyping::Flag::f_top_msg_id
|
||||
: MTPmessages_SetTyping::Flag(0)),
|
||||
key.history->peer->input,
|
||||
MTP_int(key.topMsgId),
|
||||
action
|
||||
)).done([=](const MTPBool &result, mtpRequestId requestId) {
|
||||
done(result, requestId);
|
||||
}).send();
|
||||
_requests.emplace(Key{ history, type }, requestId);
|
||||
_requests.emplace(key, requestId);
|
||||
|
||||
if (type == Type::Typing) {
|
||||
_stopTypingHistory = history;
|
||||
if (key.type == Type::Typing) {
|
||||
_stopTypingHistory = key.history;
|
||||
_stopTypingTimer.callOnce(kCancelTypingActionTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,16 @@ public:
|
||||
void update(
|
||||
not_null<History*> history,
|
||||
SendProgressType type,
|
||||
int32 progress = 0);
|
||||
int progress = 0);
|
||||
void update(
|
||||
not_null<History*> history,
|
||||
MsgId topMsgId,
|
||||
SendProgressType type,
|
||||
int progress = 0);
|
||||
void cancel(
|
||||
not_null<History*> history,
|
||||
MsgId topMsgId,
|
||||
SendProgressType type);
|
||||
void cancel(
|
||||
not_null<History*> history,
|
||||
SendProgressType type);
|
||||
@@ -64,22 +73,26 @@ public:
|
||||
private:
|
||||
struct Key {
|
||||
not_null<History*> history;
|
||||
MsgId topMsgId = 0;
|
||||
SendProgressType type = SendProgressType();
|
||||
|
||||
inline bool operator<(const Key &other) const {
|
||||
return (history < other.history)
|
||||
|| (history == other.history && type < other.type);
|
||||
|| (history == other.history && topMsgId < other.topMsgId)
|
||||
|| (history == other.history
|
||||
&& topMsgId == other.topMsgId
|
||||
&& type < other.type);
|
||||
}
|
||||
};
|
||||
|
||||
void send(
|
||||
not_null<History*> history,
|
||||
SendProgressType type,
|
||||
int32 progress);
|
||||
bool updated(const Key &key, bool doing);
|
||||
|
||||
void send(const Key &key, int progress);
|
||||
void done(const MTPBool &result, mtpRequestId requestId);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
base::flat_map<Key, mtpRequestId> _requests;
|
||||
base::flat_map<Key, crl::time> _updated;
|
||||
base::Timer _stopTypingTimer;
|
||||
History *_stopTypingHistory = nullptr;
|
||||
|
||||
|
||||
@@ -39,10 +39,12 @@ void InnerFillMessagePostFlags(
|
||||
const Api::SendOptions &options,
|
||||
not_null<PeerData*> peer,
|
||||
MTPDmessage::Flags &flags) {
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
if (!channelPost) {
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
if (!anonymousPost) {
|
||||
flags |= MTPDmessage::Flag::f_from_id;
|
||||
return;
|
||||
} else if (peer->asMegagroup()) {
|
||||
return;
|
||||
}
|
||||
flags |= MTPDmessage::Flag::f_post;
|
||||
// Don't display views and author of a new post when it's scheduled.
|
||||
@@ -79,18 +81,18 @@ void SendExistingMedia(
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||
if (message.action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
|
||||
flags |= MTPDmessage::Flag::f_reply_to;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = message.action.options.silent
|
||||
|| (channelPost && session->data().notifySilentPosts(peer));
|
||||
|| (peer->isBroadcast() && session->data().notifySilentPosts(peer));
|
||||
InnerFillMessagePostFlags(message.action.options, peer, flags);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
auto messageFromId = channelPost ? 0 : session->userId();
|
||||
auto messagePostAuthor = channelPost ? session->user()->name : QString();
|
||||
auto messageFromId = anonymousPost ? 0 : session->userPeerId();
|
||||
auto messagePostAuthor = peer->isBroadcast() ? session->user()->name : QString();
|
||||
|
||||
auto caption = TextWithEntities{
|
||||
message.textWithTags.text,
|
||||
@@ -249,18 +251,19 @@ bool SendDice(Api::MessageToSend &message) {
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||
if (message.action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
|
||||
flags |= MTPDmessage::Flag::f_reply_to;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto replyHeader = NewMessageReplyHeader(message.action);
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = message.action.options.silent
|
||||
|| (channelPost && session->data().notifySilentPosts(peer));
|
||||
|| (peer->isBroadcast() && session->data().notifySilentPosts(peer));
|
||||
InnerFillMessagePostFlags(message.action.options, peer, flags);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
auto messageFromId = channelPost ? 0 : session->userId();
|
||||
auto messagePostAuthor = channelPost ? session->user()->name : QString();
|
||||
auto messageFromId = anonymousPost ? 0 : session->userPeerId();
|
||||
auto messagePostAuthor = peer->isBroadcast() ? session->user()->name : QString();
|
||||
const auto replyTo = message.action.replyTo;
|
||||
|
||||
if (message.action.options.scheduled) {
|
||||
@@ -272,23 +275,27 @@ bool SendDice(Api::MessageToSend &message) {
|
||||
|
||||
session->data().registerMessageRandomId(randomId, newId);
|
||||
|
||||
const auto views = 1;
|
||||
const auto forwards = 0;
|
||||
history->addNewMessage(
|
||||
MTP_message(
|
||||
MTP_flags(flags),
|
||||
MTP_int(newId.msg),
|
||||
MTP_int(messageFromId),
|
||||
peerToMTP(messageFromId),
|
||||
peerToMTP(history->peer->id),
|
||||
MTPMessageFwdHeader(),
|
||||
MTP_int(0),
|
||||
MTP_int(replyTo),
|
||||
MTPint(), // via_bot_id
|
||||
replyHeader,
|
||||
MTP_int(HistoryItem::NewMessageDate(
|
||||
message.action.options.scheduled)),
|
||||
MTP_string(),
|
||||
MTP_messageMediaDice(MTP_int(0), MTP_string(emoji)),
|
||||
MTPReplyMarkup(),
|
||||
MTP_vector<MTPMessageEntity>(),
|
||||
MTP_int(1),
|
||||
MTPint(),
|
||||
MTP_int(views),
|
||||
MTP_int(forwards),
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTP_string(messagePostAuthor),
|
||||
MTPlong(),
|
||||
//MTPMessageReactions(),
|
||||
@@ -387,9 +394,10 @@ void SendConfirmedFile(
|
||||
| MTPDmessage::Flag::f_media;
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
if (file->to.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
|
||||
flags |= MTPDmessage::Flag::f_reply_to;
|
||||
}
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto replyHeader = NewMessageReplyHeader(action);
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = file->to.options.silent;
|
||||
Api::FillMessagePostFlags(action, peer, flags);
|
||||
if (silentPost) {
|
||||
@@ -406,11 +414,13 @@ void SendConfirmedFile(
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
}
|
||||
|
||||
const auto messageFromId = channelPost ? 0 : session->userId();
|
||||
const auto messagePostAuthor = channelPost
|
||||
const auto messageFromId = anonymousPost ? 0 : session->userPeerId();
|
||||
const auto messagePostAuthor = peer->isBroadcast()
|
||||
? session->user()->name
|
||||
: QString();
|
||||
|
||||
const auto views = 1;
|
||||
const auto forwards = 0;
|
||||
if (file->type == SendMediaType::Photo) {
|
||||
const auto photoFlags = MTPDmessageMediaPhoto::Flag::f_photo | 0;
|
||||
const auto photo = MTP_messageMediaPhoto(
|
||||
@@ -421,18 +431,20 @@ void SendConfirmedFile(
|
||||
const auto mtpMessage = MTP_message(
|
||||
MTP_flags(flags),
|
||||
MTP_int(newId.msg),
|
||||
MTP_int(messageFromId),
|
||||
peerToMTP(messageFromId),
|
||||
peerToMTP(file->to.peer),
|
||||
MTPMessageFwdHeader(),
|
||||
MTPint(),
|
||||
MTP_int(file->to.replyTo),
|
||||
replyHeader,
|
||||
MTP_int(HistoryItem::NewMessageDate(file->to.options.scheduled)),
|
||||
MTP_string(caption.text),
|
||||
photo,
|
||||
MTPReplyMarkup(),
|
||||
localEntities,
|
||||
MTP_int(1),
|
||||
MTPint(),
|
||||
MTP_int(views),
|
||||
MTP_int(forwards),
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTP_string(messagePostAuthor),
|
||||
MTP_long(groupId),
|
||||
//MTPMessageReactions(),
|
||||
@@ -457,18 +469,20 @@ void SendConfirmedFile(
|
||||
const auto mtpMessage = MTP_message(
|
||||
MTP_flags(flags),
|
||||
MTP_int(newId.msg),
|
||||
MTP_int(messageFromId),
|
||||
peerToMTP(messageFromId),
|
||||
peerToMTP(file->to.peer),
|
||||
MTPMessageFwdHeader(),
|
||||
MTPint(),
|
||||
MTP_int(file->to.replyTo),
|
||||
replyHeader,
|
||||
MTP_int(HistoryItem::NewMessageDate(file->to.options.scheduled)),
|
||||
MTP_string(caption.text),
|
||||
document,
|
||||
MTPReplyMarkup(),
|
||||
localEntities,
|
||||
MTP_int(1),
|
||||
MTPint(),
|
||||
MTP_int(views),
|
||||
MTP_int(forwards),
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTP_string(messagePostAuthor),
|
||||
MTP_long(groupId),
|
||||
//MTPMessageReactions(),
|
||||
@@ -496,19 +510,21 @@ void SendConfirmedFile(
|
||||
MTP_message(
|
||||
MTP_flags(flags),
|
||||
MTP_int(newId.msg),
|
||||
MTP_int(messageFromId),
|
||||
peerToMTP(messageFromId),
|
||||
peerToMTP(file->to.peer),
|
||||
MTPMessageFwdHeader(),
|
||||
MTPint(),
|
||||
MTP_int(file->to.replyTo),
|
||||
replyHeader,
|
||||
MTP_int(
|
||||
HistoryItem::NewMessageDate(file->to.options.scheduled)),
|
||||
MTP_string(caption.text),
|
||||
document,
|
||||
MTPReplyMarkup(),
|
||||
localEntities,
|
||||
MTP_int(1),
|
||||
MTPint(),
|
||||
MTP_int(views),
|
||||
MTP_int(forwards),
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTP_string(messagePostAuthor),
|
||||
MTP_long(groupId),
|
||||
//MTPMessageReactions(),
|
||||
|
||||
117
Telegram/SourceFiles/api/api_toggling_media.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_toggling_media.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
template <typename ToggleRequest, typename DoneCallback>
|
||||
void ToggleExistingMedia(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
ToggleRequest toggleRequest,
|
||||
DoneCallback &&done) {
|
||||
const auto api = &document->owner().session().api();
|
||||
|
||||
auto performRequest = [=](const auto &repeatRequest) -> void {
|
||||
const auto usedFileReference = document->fileReference();
|
||||
api->request(std::move(
|
||||
toggleRequest
|
||||
)).done([=](const MTPBool &result) {
|
||||
if (mtpIsTrue(result)) {
|
||||
done();
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
if (error.code() == 400
|
||||
&& error.type().startsWith(u"FILE_REFERENCE_"_q)) {
|
||||
auto refreshed = [=](const Data::UpdatedFileReferences &d) {
|
||||
if (document->fileReference() != usedFileReference) {
|
||||
repeatRequest(repeatRequest);
|
||||
}
|
||||
};
|
||||
api->refreshFileReference(origin, std::move(refreshed));
|
||||
}
|
||||
}).send();
|
||||
};
|
||||
performRequest(performRequest);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ToggleFavedSticker(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin) {
|
||||
ToggleFavedSticker(
|
||||
document,
|
||||
std::move(origin),
|
||||
!document->owner().stickers().isFaved(document));
|
||||
}
|
||||
|
||||
void ToggleFavedSticker(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool faved) {
|
||||
if (faved && !document->sticker()) {
|
||||
return;
|
||||
}
|
||||
ToggleExistingMedia(
|
||||
document,
|
||||
std::move(origin),
|
||||
MTPmessages_FaveSticker(document->mtpInput(), MTP_bool(!faved)),
|
||||
[=] { document->owner().stickers().setFaved(document, faved); });
|
||||
}
|
||||
|
||||
void ToggleRecentSticker(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool saved) {
|
||||
if (!document->sticker()) {
|
||||
return;
|
||||
}
|
||||
auto done = [=] {
|
||||
if (!saved) {
|
||||
document->owner().stickers().removeFromRecentSet(document);
|
||||
}
|
||||
};
|
||||
ToggleExistingMedia(
|
||||
document,
|
||||
std::move(origin),
|
||||
MTPmessages_SaveRecentSticker(
|
||||
MTP_flags(MTPmessages_SaveRecentSticker::Flag(0)),
|
||||
document->mtpInput(),
|
||||
MTP_bool(!saved)),
|
||||
std::move(done));
|
||||
}
|
||||
|
||||
void ToggleSavedGif(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool saved) {
|
||||
if (saved && !document->isGifv()) {
|
||||
return;
|
||||
}
|
||||
auto done = [=] {
|
||||
if (saved) {
|
||||
document->owner().stickers().addSavedGif(document);
|
||||
}
|
||||
};
|
||||
ToggleExistingMedia(
|
||||
document,
|
||||
std::move(origin),
|
||||
MTPmessages_SaveGif(document->mtpInput(), MTP_bool(!saved)),
|
||||
std::move(done));
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
31
Telegram/SourceFiles/api/api_toggling_media.h
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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Api {
|
||||
|
||||
void ToggleFavedSticker(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin);
|
||||
|
||||
void ToggleFavedSticker(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool faved);
|
||||
|
||||
void ToggleRecentSticker(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool saved);
|
||||
|
||||
void ToggleSavedGif(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool saved);
|
||||
|
||||
} // namespace Api
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_updates.h"
|
||||
|
||||
#include "api/api_authorizations.h"
|
||||
#include "api/api_text_entities.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_account.h"
|
||||
@@ -97,20 +98,9 @@ bool ForwardedInfoDataLoaded(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPMessageFwdHeader &header) {
|
||||
return header.match([&](const MTPDmessageFwdHeader &data) {
|
||||
if (const auto channelId = data.vchannel_id()) {
|
||||
if (!session->data().channelLoaded(channelId->v)) {
|
||||
return false;
|
||||
}
|
||||
if (const auto fromId = data.vfrom_id()) {
|
||||
const auto from = session->data().user(fromId->v);
|
||||
// Minimal loaded is fine in this case.
|
||||
if (from->loadedStatus == PeerData::NotLoaded) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (const auto fromId = data.vfrom_id()) {
|
||||
if (const auto fromId = data.vfrom_id()) {
|
||||
// Fully loaded is required in this case.
|
||||
if (!session->data().userLoaded(fromId->v)) {
|
||||
if (!session->data().peerLoaded(peerFromMTP(*fromId))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -145,7 +135,7 @@ DataIsLoadedResult AllDataLoadedForMessage(
|
||||
return message.match([&](const MTPDmessage &message) {
|
||||
if (const auto fromId = message.vfrom_id()) {
|
||||
if (!message.is_post()
|
||||
&& !session->data().userLoaded(fromId->v)) {
|
||||
&& !session->data().peerLoaded(peerFromMTP(*fromId))) {
|
||||
return DataIsLoadedResult::FromNotLoaded;
|
||||
}
|
||||
}
|
||||
@@ -168,7 +158,7 @@ DataIsLoadedResult AllDataLoadedForMessage(
|
||||
}, [&](const MTPDmessageService &message) {
|
||||
if (const auto fromId = message.vfrom_id()) {
|
||||
if (!message.is_post()
|
||||
&& !session->data().userLoaded(fromId->v)) {
|
||||
&& !session->data().peerLoaded(peerFromMTP(*fromId))) {
|
||||
return DataIsLoadedResult::FromNotLoaded;
|
||||
}
|
||||
}
|
||||
@@ -890,23 +880,26 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||
const auto peerUserId = d.is_out()
|
||||
? d.vuser_id()
|
||||
: MTP_int(_session->userId());
|
||||
const auto fwd = d.vfwd_from();
|
||||
_session->data().addNewMessage(
|
||||
MTP_message(
|
||||
MTP_flags(flags),
|
||||
d.vid(),
|
||||
d.is_out() ? MTP_int(_session->userId()) : d.vuser_id(),
|
||||
MTP_peerUser(peerUserId),
|
||||
fwd ? (*fwd) : MTPMessageFwdHeader(),
|
||||
(d.is_out()
|
||||
? peerToMTP(_session->userPeerId())
|
||||
: MTP_peerUser(d.vuser_id())),
|
||||
MTP_peerUser(d.vuser_id()),
|
||||
d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(),
|
||||
MTP_int(d.vvia_bot_id().value_or_empty()),
|
||||
MTP_int(d.vreply_to_msg_id().value_or_empty()),
|
||||
d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(),
|
||||
d.vdate(),
|
||||
d.vmessage(),
|
||||
MTP_messageMediaEmpty(),
|
||||
MTPReplyMarkup(),
|
||||
MTP_vector<MTPMessageEntity>(d.ventities().value_or_empty()),
|
||||
MTPint(),
|
||||
MTPint(),
|
||||
MTPint(), // views
|
||||
MTPint(), // forwards
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTPstring(),
|
||||
MTPlong(),
|
||||
//MTPMessageReactions(),
|
||||
@@ -917,24 +910,26 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||
|
||||
case mtpc_updateShortChatMessage: {
|
||||
const auto &d = updates.c_updateShortChatMessage();
|
||||
const auto flags = mtpCastFlags(d.vflags().v) | MTPDmessage::Flag::f_from_id;
|
||||
const auto fwd = d.vfwd_from();
|
||||
const auto flags = mtpCastFlags(d.vflags().v)
|
||||
| MTPDmessage::Flag::f_from_id;
|
||||
_session->data().addNewMessage(
|
||||
MTP_message(
|
||||
MTP_flags(flags),
|
||||
d.vid(),
|
||||
d.vfrom_id(),
|
||||
MTP_peerUser(d.vfrom_id()),
|
||||
MTP_peerChat(d.vchat_id()),
|
||||
fwd ? (*fwd) : MTPMessageFwdHeader(),
|
||||
d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(),
|
||||
MTP_int(d.vvia_bot_id().value_or_empty()),
|
||||
MTP_int(d.vreply_to_msg_id().value_or_empty()),
|
||||
d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(),
|
||||
d.vdate(),
|
||||
d.vmessage(),
|
||||
MTP_messageMediaEmpty(),
|
||||
MTPReplyMarkup(),
|
||||
MTP_vector<MTPMessageEntity>(d.ventities().value_or_empty()),
|
||||
MTPint(),
|
||||
MTPint(),
|
||||
MTPint(), // views
|
||||
MTPint(), // forwards
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTPstring(),
|
||||
MTPlong(),
|
||||
//MTPMessageReactions(),
|
||||
@@ -1541,26 +1536,51 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
const auto user = session().data().userLoaded(d.vuser_id().v);
|
||||
if (history && user) {
|
||||
const auto when = requestingDifference() ? 0 : base::unixtime::now();
|
||||
session().data().registerSendAction(history, user, d.vaction(), when);
|
||||
session().data().registerSendAction(
|
||||
history,
|
||||
MsgId(),
|
||||
user,
|
||||
d.vaction(),
|
||||
when);
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateChatUserTyping: {
|
||||
auto &d = update.c_updateChatUserTyping();
|
||||
const auto history = [&]() -> History* {
|
||||
if (const auto chat = session().data().chatLoaded(d.vchat_id().v)) {
|
||||
return session().data().historyLoaded(chat->id);
|
||||
} else if (const auto channel = session().data().channelLoaded(d.vchat_id().v)) {
|
||||
return session().data().historyLoaded(channel->id);
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
const auto history = session().data().historyLoaded(
|
||||
peerFromChat(d.vchat_id()));
|
||||
const auto user = (d.vuser_id().v == session().userId())
|
||||
? nullptr
|
||||
: session().data().userLoaded(d.vuser_id().v);
|
||||
if (history && user) {
|
||||
const auto when = requestingDifference() ? 0 : base::unixtime::now();
|
||||
session().data().registerSendAction(history, user, d.vaction(), when);
|
||||
session().data().registerSendAction(
|
||||
history,
|
||||
MsgId(),
|
||||
user,
|
||||
d.vaction(),
|
||||
when);
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelUserTyping: {
|
||||
const auto &d = update.c_updateChannelUserTyping();
|
||||
const auto history = session().data().historyLoaded(
|
||||
peerFromChannel(d.vchannel_id()));
|
||||
const auto user = (d.vuser_id().v == session().userId())
|
||||
? nullptr
|
||||
: session().data().userLoaded(d.vuser_id().v);
|
||||
if (history && user) {
|
||||
const auto when = requestingDifference()
|
||||
? 0
|
||||
: base::unixtime::now();
|
||||
const auto rootId = d.vtop_msg_id().value_or_empty();
|
||||
session().data().registerSendAction(
|
||||
history,
|
||||
rootId,
|
||||
user,
|
||||
d.vaction(),
|
||||
when);
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -1731,10 +1751,10 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
Core::App().calls().handleUpdate(&session(), update);
|
||||
} break;
|
||||
|
||||
case mtpc_updateUserBlocked: {
|
||||
const auto &d = update.c_updateUserBlocked();
|
||||
if (const auto user = session().data().userLoaded(d.vuser_id().v)) {
|
||||
user->setIsBlocked(mtpIsTrue(d.vblocked()));
|
||||
case mtpc_updatePeerBlocked: {
|
||||
const auto &d = update.c_updatePeerBlocked();
|
||||
if (const auto peer = session().data().peerLoaded(peerFromMTP(d.vpeer_id()))) {
|
||||
peer->setIsBlocked(mtpIsTrue(d.vblocked()));
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -1753,7 +1773,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
} else {
|
||||
session().data().serviceNotification(text, d.vmedia());
|
||||
session().data().checkNewAuthorization();
|
||||
session().api().authorizations().reload();
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -1912,12 +1932,54 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelMessageViews: {
|
||||
auto &d = update.c_updateChannelMessageViews();
|
||||
if (auto item = session().data().message(d.vchannel_id().v, d.vid().v)) {
|
||||
const auto &d = update.c_updateChannelMessageViews();
|
||||
if (const auto item = session().data().message(d.vchannel_id().v, d.vid().v)) {
|
||||
item->setViewsCount(d.vviews().v);
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelMessageForwards: {
|
||||
const auto &d = update.c_updateChannelMessageForwards();
|
||||
if (const auto item = session().data().message(d.vchannel_id().v, d.vid().v)) {
|
||||
item->setForwardsCount(d.vforwards().v);
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateReadChannelDiscussionInbox: {
|
||||
const auto &d = update.c_updateReadChannelDiscussionInbox();
|
||||
const auto channelId = d.vchannel_id().v;
|
||||
const auto msgId = d.vtop_msg_id().v;
|
||||
const auto readTillId = d.vread_max_id().v;
|
||||
const auto item = session().data().message(channelId, msgId);
|
||||
if (item) {
|
||||
item->setRepliesInboxReadTill(readTillId);
|
||||
if (const auto post = item->lookupDiscussionPostOriginal()) {
|
||||
post->setRepliesInboxReadTill(readTillId);
|
||||
}
|
||||
}
|
||||
if (const auto broadcastId = d.vbroadcast_id()) {
|
||||
if (const auto post = session().data().message(
|
||||
broadcastId->v,
|
||||
d.vbroadcast_post()->v)) {
|
||||
post->setRepliesInboxReadTill(readTillId);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateReadChannelDiscussionOutbox: {
|
||||
const auto &d = update.c_updateReadChannelDiscussionOutbox();
|
||||
const auto channelId = d.vchannel_id().v;
|
||||
const auto msgId = d.vtop_msg_id().v;
|
||||
const auto readTillId = d.vread_max_id().v;
|
||||
const auto item = session().data().message(channelId, msgId);
|
||||
if (item) {
|
||||
item->setRepliesOutboxReadTill(readTillId);
|
||||
if (const auto post = item->lookupDiscussionPostOriginal()) {
|
||||
post->setRepliesOutboxReadTill(readTillId);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelAvailableMessages: {
|
||||
auto &d = update.c_updateChannelAvailableMessages();
|
||||
if (const auto channel = session().data().channelLoaded(d.vchannel_id().v)) {
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "apiwrap.h"
|
||||
|
||||
#include "api/api_authorizations.h"
|
||||
#include "api/api_hash.h"
|
||||
#include "api/api_media.h"
|
||||
#include "api/api_sending.h"
|
||||
@@ -158,19 +159,19 @@ std::optional<ApiWrap::Privacy::Key> ApiWrap::Privacy::KeyFromMTP(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool ApiWrap::BlockedUsersSlice::Item::operator==(const Item &other) const {
|
||||
return (user == other.user) && (date == other.date);
|
||||
bool ApiWrap::BlockedPeersSlice::Item::operator==(const Item &other) const {
|
||||
return (peer == other.peer) && (date == other.date);
|
||||
}
|
||||
|
||||
bool ApiWrap::BlockedUsersSlice::Item::operator!=(const Item &other) const {
|
||||
bool ApiWrap::BlockedPeersSlice::Item::operator!=(const Item &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool ApiWrap::BlockedUsersSlice::operator==(const BlockedUsersSlice &other) const {
|
||||
bool ApiWrap::BlockedPeersSlice::operator==(const BlockedPeersSlice &other) const {
|
||||
return (total == other.total) && (list == other.list);
|
||||
}
|
||||
|
||||
bool ApiWrap::BlockedUsersSlice::operator!=(const BlockedUsersSlice &other) const {
|
||||
bool ApiWrap::BlockedPeersSlice::operator!=(const BlockedPeersSlice &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
@@ -186,6 +187,7 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||
//, _feedReadTimer([=] { readFeeds(); }) // #feed
|
||||
, _topPromotionTimer([=] { refreshTopPromotion(); })
|
||||
, _updateNotifySettingsTimer([=] { sendNotifySettingsUpdates(); })
|
||||
, _authorizations(std::make_unique<Api::Authorizations>(this))
|
||||
, _selfDestruct(std::make_unique<Api::SelfDestruct>(this))
|
||||
, _sensitiveContent(std::make_unique<Api::SensitiveContent>(this))
|
||||
, _globalPrivacy(std::make_unique<Api::GlobalPrivacy>(this)) {
|
||||
@@ -683,17 +685,57 @@ void ApiWrap::finalizeMessageDataRequest(
|
||||
}
|
||||
}
|
||||
|
||||
QString ApiWrap::exportDirectMessageLink(not_null<HistoryItem*> item) {
|
||||
QString ApiWrap::exportDirectMessageLink(
|
||||
not_null<HistoryItem*> item,
|
||||
bool inRepliesContext) {
|
||||
Expects(item->history()->peer->isChannel());
|
||||
|
||||
const auto itemId = item->fullId();
|
||||
const auto channel = item->history()->peer->asChannel();
|
||||
const auto fallback = [&] {
|
||||
const auto base = channel->hasUsername()
|
||||
? channel->username
|
||||
: "c/" + QString::number(channel->bareId());
|
||||
const auto query = base + '/' + QString::number(item->id);
|
||||
if (channel->hasUsername() && !channel->isMegagroup()) {
|
||||
auto linkChannel = channel;
|
||||
auto linkItemId = item->id;
|
||||
auto linkCommentId = 0;
|
||||
auto linkThreadId = 0;
|
||||
if (inRepliesContext) {
|
||||
if (const auto rootId = item->replyToTop()) {
|
||||
const auto root = item->history()->owner().message(
|
||||
channel->bareId(),
|
||||
rootId);
|
||||
const auto sender = root
|
||||
? root->discussionPostOriginalSender()
|
||||
: nullptr;
|
||||
if (sender && sender->hasUsername()) {
|
||||
// Comment to a public channel.
|
||||
const auto forwarded = root->Get<HistoryMessageForwarded>();
|
||||
linkItemId = forwarded->savedFromMsgId;
|
||||
if (linkItemId) {
|
||||
linkChannel = sender;
|
||||
linkCommentId = item->id;
|
||||
} else {
|
||||
linkItemId = item->id;
|
||||
}
|
||||
} else {
|
||||
// Reply in a thread, maybe comment in a private channel.
|
||||
linkThreadId = rootId;
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto base = linkChannel->hasUsername()
|
||||
? linkChannel->username
|
||||
: "c/" + QString::number(linkChannel->bareId());
|
||||
const auto query = base
|
||||
+ '/'
|
||||
+ QString::number(linkItemId)
|
||||
+ (linkCommentId
|
||||
? "?comment=" + QString::number(linkCommentId)
|
||||
: linkThreadId
|
||||
? "?thread=" + QString::number(linkThreadId)
|
||||
: "");
|
||||
if (linkChannel->hasUsername()
|
||||
&& !linkChannel->isMegagroup()
|
||||
&& !linkCommentId
|
||||
&& !linkThreadId) {
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto document = media->document()) {
|
||||
if (document->isVideoMessage()) {
|
||||
@@ -709,9 +751,11 @@ QString ApiWrap::exportDirectMessageLink(not_null<HistoryItem*> item) {
|
||||
? i->second
|
||||
: fallback();
|
||||
request(MTPchannels_ExportMessageLink(
|
||||
MTP_flags(inRepliesContext
|
||||
? MTPchannels_ExportMessageLink::Flag::f_thread
|
||||
: MTPchannels_ExportMessageLink::Flag(0)),
|
||||
channel->inputChannel,
|
||||
MTP_int(item->id),
|
||||
MTP_bool(false)
|
||||
MTP_int(item->id)
|
||||
)).done([=](const MTPExportedMessageLink &result) {
|
||||
const auto link = result.match([&](const auto &data) {
|
||||
return qs(data.vlink());
|
||||
@@ -1495,9 +1539,13 @@ void ApiWrap::applyLastParticipantsList(
|
||||
});
|
||||
const auto adminCanEdit = (p.type() == mtpc_channelParticipantAdmin)
|
||||
? p.c_channelParticipantAdmin().is_can_edit()
|
||||
: (p.type() == mtpc_channelParticipantCreator)
|
||||
? channel->amCreator()
|
||||
: false;
|
||||
const auto adminRights = (p.type() == mtpc_channelParticipantAdmin)
|
||||
? p.c_channelParticipantAdmin().vadmin_rights()
|
||||
: (p.type() == mtpc_channelParticipantCreator)
|
||||
? p.c_channelParticipantCreator().vadmin_rights()
|
||||
: emptyAdminRights;
|
||||
const auto restrictedRights = (p.type() == mtpc_channelParticipantBanned)
|
||||
? p.c_channelParticipantBanned().vbanned_rights()
|
||||
@@ -2003,64 +2051,66 @@ void ApiWrap::leaveChannel(not_null<ChannelData*> channel) {
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::blockUser(not_null<UserData*> user) {
|
||||
if (user->isBlocked()) {
|
||||
void ApiWrap::blockPeer(not_null<PeerData*> peer) {
|
||||
if (peer->isBlocked()) {
|
||||
session().changes().peerUpdated(
|
||||
user,
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::IsBlocked);
|
||||
} else if (_blockRequests.find(user) == end(_blockRequests)) {
|
||||
const auto requestId = request(MTPcontacts_Block(user->inputUser)).done([this, user](const MTPBool &result) {
|
||||
_blockRequests.erase(user);
|
||||
user->setIsBlocked(true);
|
||||
if (_blockedUsersSlice) {
|
||||
_blockedUsersSlice->list.insert(
|
||||
_blockedUsersSlice->list.begin(),
|
||||
{ user, base::unixtime::now() });
|
||||
++_blockedUsersSlice->total;
|
||||
_blockedUsersChanges.fire_copy(*_blockedUsersSlice);
|
||||
} else if (_blockRequests.find(peer) == end(_blockRequests)) {
|
||||
const auto requestId = request(MTPcontacts_Block(
|
||||
peer->input
|
||||
)).done([=](const MTPBool &result) {
|
||||
_blockRequests.erase(peer);
|
||||
peer->setIsBlocked(true);
|
||||
if (_blockedPeersSlice) {
|
||||
_blockedPeersSlice->list.insert(
|
||||
_blockedPeersSlice->list.begin(),
|
||||
{ peer, base::unixtime::now() });
|
||||
++_blockedPeersSlice->total;
|
||||
_blockedPeersChanges.fire_copy(*_blockedPeersSlice);
|
||||
}
|
||||
}).fail([this, user](const RPCError &error) {
|
||||
_blockRequests.erase(user);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_blockRequests.erase(peer);
|
||||
}).send();
|
||||
|
||||
_blockRequests.emplace(user, requestId);
|
||||
_blockRequests.emplace(peer, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::unblockUser(not_null<UserData*> user, Fn<void()> onDone) {
|
||||
if (!user->isBlocked()) {
|
||||
void ApiWrap::unblockPeer(not_null<PeerData*> peer, Fn<void()> onDone) {
|
||||
if (!peer->isBlocked()) {
|
||||
session().changes().peerUpdated(
|
||||
user,
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::IsBlocked);
|
||||
return;
|
||||
} else if (_blockRequests.find(user) != end(_blockRequests)) {
|
||||
} else if (_blockRequests.find(peer) != end(_blockRequests)) {
|
||||
return;
|
||||
}
|
||||
const auto requestId = request(MTPcontacts_Unblock(
|
||||
user->inputUser
|
||||
peer->input
|
||||
)).done([=](const MTPBool &result) {
|
||||
_blockRequests.erase(user);
|
||||
user->setIsBlocked(false);
|
||||
if (_blockedUsersSlice) {
|
||||
auto &list = _blockedUsersSlice->list;
|
||||
_blockRequests.erase(peer);
|
||||
peer->setIsBlocked(false);
|
||||
if (_blockedPeersSlice) {
|
||||
auto &list = _blockedPeersSlice->list;
|
||||
for (auto i = list.begin(); i != list.end(); ++i) {
|
||||
if (i->user == user) {
|
||||
if (i->peer == peer) {
|
||||
list.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_blockedUsersSlice->total > list.size()) {
|
||||
--_blockedUsersSlice->total;
|
||||
if (_blockedPeersSlice->total > list.size()) {
|
||||
--_blockedPeersSlice->total;
|
||||
}
|
||||
_blockedUsersChanges.fire_copy(*_blockedUsersSlice);
|
||||
_blockedPeersChanges.fire_copy(*_blockedPeersSlice);
|
||||
}
|
||||
if (onDone) {
|
||||
onDone();
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
_blockRequests.erase(user);
|
||||
_blockRequests.erase(peer);
|
||||
}).send();
|
||||
_blockRequests.emplace(user, requestId);
|
||||
_blockRequests.emplace(peer, requestId);
|
||||
}
|
||||
|
||||
void ApiWrap::exportInviteLink(not_null<PeerData*> peer) {
|
||||
@@ -2203,7 +2253,7 @@ void ApiWrap::handlePrivacyChange(
|
||||
void ApiWrap::updatePrivacyLastSeens(const QVector<MTPPrivacyRule> &rules) {
|
||||
const auto now = base::unixtime::now();
|
||||
_session->data().enumerateUsers([&](UserData *user) {
|
||||
if (user->isSelf() || user->loadedStatus != PeerData::FullLoaded) {
|
||||
if (user->isSelf() || !user->isFullLoaded()) {
|
||||
return;
|
||||
}
|
||||
if (user->onlineTill <= 0) {
|
||||
@@ -2682,14 +2732,14 @@ void ApiWrap::requestFileReference(
|
||||
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87122
|
||||
const auto &origin = p.first;
|
||||
const auto &reference = p.second;
|
||||
const auto documentId = base::get_if<DocumentFileLocationId>(
|
||||
const auto documentId = std::get_if<DocumentFileLocationId>(
|
||||
&origin);
|
||||
if (documentId) {
|
||||
_session->data().document(
|
||||
documentId->id
|
||||
)->refreshFileReference(reference);
|
||||
}
|
||||
const auto photoId = base::get_if<PhotoFileLocationId>(&origin);
|
||||
const auto photoId = std::get_if<PhotoFileLocationId>(&origin);
|
||||
if (photoId) {
|
||||
_session->data().photo(
|
||||
photoId->id
|
||||
@@ -2748,7 +2798,7 @@ void ApiWrap::refreshFileReference(
|
||||
const auto fail = [&] {
|
||||
handler(UpdatedFileReferences());
|
||||
};
|
||||
origin.data.match([&](Data::FileOriginMessage data) {
|
||||
v::match(origin.data, [&](Data::FileOriginMessage data) {
|
||||
if (const auto item = _session->data().message(data)) {
|
||||
if (item->isScheduled()) {
|
||||
const auto &scheduled = _session->data().scheduledMessages();
|
||||
@@ -2824,7 +2874,7 @@ void ApiWrap::refreshFileReference(
|
||||
MTP_long(data.themeId),
|
||||
MTP_long(data.accessHash)),
|
||||
MTP_long(0)));
|
||||
}, [&](std::nullopt_t) {
|
||||
}, [&](v::null_t) {
|
||||
fail();
|
||||
});
|
||||
}
|
||||
@@ -2936,72 +2986,6 @@ std::vector<not_null<DocumentData*>> *ApiWrap::stickersByEmoji(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ApiWrap::toggleFavedSticker(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool faved) {
|
||||
if (faved && !document->sticker()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto performRequest = [=](const auto &repeatRequest) -> void {
|
||||
const auto usedFileReference = document->fileReference();
|
||||
request(MTPmessages_FaveSticker(
|
||||
document->mtpInput(),
|
||||
MTP_bool(!faved)
|
||||
)).done([=](const MTPBool &result) {
|
||||
if (mtpIsTrue(result)) {
|
||||
_session->data().stickers().setFaved(document, faved);
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
if (error.code() == 400
|
||||
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
|
||||
auto refreshed = [=](const UpdatedFileReferences &data) {
|
||||
if (document->fileReference() != usedFileReference) {
|
||||
repeatRequest(repeatRequest);
|
||||
}
|
||||
};
|
||||
refreshFileReference(origin, std::move(refreshed));
|
||||
}
|
||||
}).send();
|
||||
};
|
||||
performRequest(performRequest);
|
||||
}
|
||||
|
||||
void ApiWrap::toggleSavedGif(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool saved) {
|
||||
if (saved && !document->isGifv()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto performRequest = [=](const auto &repeatRequest) -> void {
|
||||
const auto usedFileReference = document->fileReference();
|
||||
request(MTPmessages_SaveGif(
|
||||
document->mtpInput(),
|
||||
MTP_bool(!saved)
|
||||
)).done([=](const MTPBool &result) {
|
||||
if (mtpIsTrue(result)) {
|
||||
if (saved) {
|
||||
_session->data().stickers().addSavedGif(document);
|
||||
}
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
if (error.code() == 400
|
||||
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
|
||||
auto refreshed = [=](const UpdatedFileReferences &data) {
|
||||
if (document->fileReference() != usedFileReference) {
|
||||
repeatRequest(repeatRequest);
|
||||
}
|
||||
};
|
||||
refreshFileReference(origin, std::move(refreshed));
|
||||
}
|
||||
}).send();
|
||||
};
|
||||
performRequest(performRequest);
|
||||
}
|
||||
|
||||
void ApiWrap::requestStickers(TimeId now) {
|
||||
if (!_session->data().stickers().updateNeeded(now)
|
||||
|| _stickersUpdateRequest) {
|
||||
@@ -3962,9 +3946,9 @@ void ApiWrap::forwardMessages(
|
||||
|
||||
histories.readInbox(history);
|
||||
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = action.options.silent
|
||||
|| (channelPost && _session->data().notifySilentPosts(peer));
|
||||
|| (peer->isBroadcast() && _session->data().notifySilentPosts(peer));
|
||||
|
||||
auto flags = MTPDmessage::Flags(0);
|
||||
auto clientFlags = MTPDmessage_ClientFlags();
|
||||
@@ -3981,7 +3965,6 @@ void ApiWrap::forwardMessages(
|
||||
}
|
||||
|
||||
auto forwardFrom = items.front()->history()->peer;
|
||||
auto currentGroupId = items.front()->groupId();
|
||||
auto ids = QVector<MTPint>();
|
||||
auto randomIds = QVector<MTPlong>();
|
||||
auto localIds = std::shared_ptr<base::flat_map<uint64, FullMsgId>>();
|
||||
@@ -3990,14 +3973,10 @@ void ApiWrap::forwardMessages(
|
||||
if (shared) {
|
||||
++shared->requestsLeft;
|
||||
}
|
||||
const auto finalFlags = sendFlags
|
||||
| (currentGroupId == MessageGroupId()
|
||||
? MTPmessages_ForwardMessages::Flag(0)
|
||||
: MTPmessages_ForwardMessages::Flag::f_grouped);
|
||||
const auto requestType = Data::Histories::RequestType::Send;
|
||||
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||
history->sendRequestId = request(MTPmessages_ForwardMessages(
|
||||
MTP_flags(finalFlags),
|
||||
MTP_flags(sendFlags),
|
||||
forwardFrom->input,
|
||||
MTP_vector<MTPint>(ids),
|
||||
MTP_vector<MTPlong>(randomIds),
|
||||
@@ -4039,10 +4018,10 @@ void ApiWrap::forwardMessages(
|
||||
peerToChannel(peer->id),
|
||||
_session->data().nextLocalMessageId());
|
||||
const auto self = _session->user();
|
||||
const auto messageFromId = channelPost
|
||||
? UserId(0)
|
||||
: peerToUser(self->id);
|
||||
const auto messagePostAuthor = channelPost
|
||||
const auto messageFromId = anonymousPost
|
||||
? PeerId(0)
|
||||
: self->id;
|
||||
const auto messagePostAuthor = peer->isBroadcast()
|
||||
? self->name
|
||||
: QString();
|
||||
history->addNewLocalMessage(
|
||||
@@ -4062,11 +4041,9 @@ void ApiWrap::forwardMessages(
|
||||
}
|
||||
const auto newFrom = item->history()->peer;
|
||||
const auto newGroupId = item->groupId();
|
||||
if (forwardFrom != newFrom
|
||||
|| currentGroupId != newGroupId) {
|
||||
if (forwardFrom != newFrom) {
|
||||
sendAccumulated();
|
||||
forwardFrom = newFrom;
|
||||
currentGroupId = newGroupId;
|
||||
}
|
||||
ids.push_back(MTP_int(item->id));
|
||||
randomIds.push_back(MTP_long(randomId));
|
||||
@@ -4114,34 +4091,36 @@ void ApiWrap::sendSharedContact(
|
||||
const auto newId = FullMsgId(
|
||||
history->channelId(),
|
||||
_session->data().nextLocalMessageId());
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
|
||||
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
if (action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
|
||||
flags |= MTPDmessage::Flag::f_reply_to;
|
||||
}
|
||||
const auto replyHeader = NewMessageReplyHeader(action);
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
}
|
||||
const auto messageFromId = channelPost ? 0 : _session->userId();
|
||||
const auto messagePostAuthor = channelPost
|
||||
const auto messageFromId = anonymousPost ? 0 : _session->userPeerId();
|
||||
const auto messagePostAuthor = peer->isBroadcast()
|
||||
? _session->user()->name
|
||||
: QString();
|
||||
const auto vcard = QString();
|
||||
const auto views = 1;
|
||||
const auto forwards = 0;
|
||||
const auto item = history->addNewMessage(
|
||||
MTP_message(
|
||||
MTP_flags(flags),
|
||||
MTP_int(newId.msg),
|
||||
MTP_int(messageFromId),
|
||||
peerToMTP(messageFromId),
|
||||
peerToMTP(peer->id),
|
||||
MTPMessageFwdHeader(),
|
||||
MTPint(),
|
||||
MTP_int(action.replyTo),
|
||||
MTPint(), // via_bot_id
|
||||
replyHeader,
|
||||
MTP_int(HistoryItem::NewMessageDate(action.options.scheduled)),
|
||||
MTP_string(),
|
||||
MTP_messageMediaContact(
|
||||
@@ -4153,7 +4132,9 @@ void ApiWrap::sendSharedContact(
|
||||
MTPReplyMarkup(),
|
||||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(views),
|
||||
MTPint(),
|
||||
MTP_int(forwards),
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTP_string(messagePostAuthor),
|
||||
MTPlong(),
|
||||
//MTPMessageReactions(),
|
||||
@@ -4167,7 +4148,9 @@ void ApiWrap::sendSharedContact(
|
||||
MTP_string(lastName),
|
||||
MTP_string(vcard));
|
||||
auto options = action.options;
|
||||
options.silent = _session->data().notifySilentPosts(peer);
|
||||
if (_session->data().notifySilentPosts(peer)) {
|
||||
options.silent = true;
|
||||
}
|
||||
sendMedia(item, media, options);
|
||||
|
||||
_session->data().sendHistoryChangeNotifications();
|
||||
@@ -4375,9 +4358,10 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto sendFlags = MTPmessages_SendMessage::Flags(0);
|
||||
if (action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
|
||||
flags |= MTPDmessage::Flag::f_reply_to;
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
const auto replyHeader = NewMessageReplyHeader(action);
|
||||
MTPMessageMedia media = MTP_messageMediaEmpty();
|
||||
if (message.webPageId == CancelledWebPageId) {
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_no_webpage;
|
||||
@@ -4389,9 +4373,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
MTP_int(page->pendingTill)));
|
||||
flags |= MTPDmessage::Flag::f_media;
|
||||
}
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = action.options.silent
|
||||
|| (channelPost && _session->data().notifySilentPosts(peer));
|
||||
|| (peer->isBroadcast() && _session->data().notifySilentPosts(peer));
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_silent;
|
||||
@@ -4411,8 +4395,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
history->clearCloudDraft();
|
||||
history->setSentDraftText(QString());
|
||||
}
|
||||
auto messageFromId = channelPost ? 0 : _session->userId();
|
||||
auto messagePostAuthor = channelPost
|
||||
auto messageFromId = anonymousPost ? 0 : _session->userPeerId();
|
||||
auto messagePostAuthor = peer->isBroadcast()
|
||||
? _session->user()->name
|
||||
: QString();
|
||||
if (action.options.scheduled) {
|
||||
@@ -4421,23 +4405,27 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
}
|
||||
const auto views = 1;
|
||||
const auto forwards = 0;
|
||||
lastMessage = history->addNewMessage(
|
||||
MTP_message(
|
||||
MTP_flags(flags),
|
||||
MTP_int(newId.msg),
|
||||
MTP_int(messageFromId),
|
||||
peerToMTP(messageFromId),
|
||||
peerToMTP(peer->id),
|
||||
MTPMessageFwdHeader(),
|
||||
MTPint(),
|
||||
MTP_int(action.replyTo),
|
||||
MTPint(), // via_bot_id
|
||||
replyHeader,
|
||||
MTP_int(
|
||||
HistoryItem::NewMessageDate(action.options.scheduled)),
|
||||
msgText,
|
||||
media,
|
||||
MTPReplyMarkup(),
|
||||
localEntities,
|
||||
MTP_int(1),
|
||||
MTPint(),
|
||||
MTP_int(views),
|
||||
MTP_int(forwards),
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTP_string(messagePostAuthor),
|
||||
MTPlong(),
|
||||
//MTPMessageReactions(),
|
||||
@@ -4524,12 +4512,12 @@ void ApiWrap::sendInlineResult(
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto sendFlags = MTPmessages_SendInlineBotResult::Flag::f_clear_draft | 0;
|
||||
if (action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
|
||||
flags |= MTPDmessage::Flag::f_reply_to;
|
||||
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
bool channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
bool silentPost = action.options.silent
|
||||
|| (channelPost && _session->data().notifySilentPosts(peer));
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = action.options.silent
|
||||
|| (peer->isBroadcast() && _session->data().notifySilentPosts(peer));
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_silent;
|
||||
@@ -4544,8 +4532,8 @@ void ApiWrap::sendInlineResult(
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
}
|
||||
|
||||
const auto messageFromId = channelPost ? 0 : _session->userId();
|
||||
const auto messagePostAuthor = channelPost
|
||||
const auto messageFromId = anonymousPost ? 0 : _session->userPeerId();
|
||||
const auto messagePostAuthor = peer->isBroadcast()
|
||||
? _session->user()->name
|
||||
: QString();
|
||||
|
||||
@@ -5184,34 +5172,34 @@ auto ApiWrap::privacyValue(Privacy::Key key) -> rpl::producer<Privacy> {
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::reloadBlockedUsers() {
|
||||
if (_blockedUsersRequestId) {
|
||||
void ApiWrap::reloadBlockedPeers() {
|
||||
if (_blockedPeersRequestId) {
|
||||
return;
|
||||
}
|
||||
_blockedUsersRequestId = request(MTPcontacts_GetBlocked(
|
||||
_blockedPeersRequestId = request(MTPcontacts_GetBlocked(
|
||||
MTP_int(0),
|
||||
MTP_int(kBlockedFirstSlice)
|
||||
)).done([=](const MTPcontacts_Blocked &result) {
|
||||
_blockedUsersRequestId = 0;
|
||||
_blockedPeersRequestId = 0;
|
||||
const auto push = [&](
|
||||
int count,
|
||||
const QVector<MTPContactBlocked> &list) {
|
||||
auto slice = BlockedUsersSlice();
|
||||
const QVector<MTPPeerBlocked> &list) {
|
||||
auto slice = BlockedPeersSlice();
|
||||
slice.total = std::max(count, list.size());
|
||||
slice.list.reserve(list.size());
|
||||
for (const auto &contact : list) {
|
||||
contact.match([&](const MTPDcontactBlocked &data) {
|
||||
const auto user = _session->data().userLoaded(
|
||||
data.vuser_id().v);
|
||||
if (user) {
|
||||
user->setIsBlocked(true);
|
||||
slice.list.push_back({ user, data.vdate().v });
|
||||
contact.match([&](const MTPDpeerBlocked &data) {
|
||||
const auto peer = _session->data().peerLoaded(
|
||||
peerFromMTP(data.vpeer_id()));
|
||||
if (peer) {
|
||||
peer->setIsBlocked(true);
|
||||
slice.list.push_back({ peer, data.vdate().v });
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!_blockedUsersSlice || *_blockedUsersSlice != slice) {
|
||||
_blockedUsersSlice = slice;
|
||||
_blockedUsersChanges.fire(std::move(slice));
|
||||
if (!_blockedPeersSlice || *_blockedPeersSlice != slice) {
|
||||
_blockedPeersSlice = slice;
|
||||
_blockedPeersChanges.fire(std::move(slice));
|
||||
}
|
||||
};
|
||||
result.match([&](const MTPDcontacts_blockedSlice &data) {
|
||||
@@ -5222,17 +5210,21 @@ void ApiWrap::reloadBlockedUsers() {
|
||||
push(0, data.vblocked().v);
|
||||
});
|
||||
}).fail([=](const RPCError &error) {
|
||||
_blockedUsersRequestId = 0;
|
||||
_blockedPeersRequestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
auto ApiWrap::blockedUsersSlice() -> rpl::producer<BlockedUsersSlice> {
|
||||
if (!_blockedUsersSlice) {
|
||||
reloadBlockedUsers();
|
||||
auto ApiWrap::blockedPeersSlice() -> rpl::producer<BlockedPeersSlice> {
|
||||
if (!_blockedPeersSlice) {
|
||||
reloadBlockedPeers();
|
||||
}
|
||||
return _blockedUsersSlice
|
||||
? _blockedUsersChanges.events_starting_with_copy(*_blockedUsersSlice)
|
||||
: (_blockedUsersChanges.events() | rpl::type_erased());
|
||||
return _blockedPeersSlice
|
||||
? _blockedPeersChanges.events_starting_with_copy(*_blockedPeersSlice)
|
||||
: (_blockedPeersChanges.events() | rpl::type_erased());
|
||||
}
|
||||
|
||||
Api::Authorizations &ApiWrap::authorizations() {
|
||||
return *_authorizations;
|
||||
}
|
||||
|
||||
Api::SelfDestruct &ApiWrap::selfDestruct() {
|
||||
@@ -5265,9 +5257,8 @@ void ApiWrap::createPoll(
|
||||
history->clearLocalDraft();
|
||||
history->clearCloudDraft();
|
||||
}
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto silentPost = action.options.silent
|
||||
|| (channelPost && _session->data().notifySilentPosts(peer));
|
||||
|| (peer->isBroadcast() && _session->data().notifySilentPosts(peer));
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ struct CloudPasswordState;
|
||||
namespace Api {
|
||||
|
||||
class Updates;
|
||||
class Authorizations;
|
||||
class SelfDestruct;
|
||||
class SensitiveContent;
|
||||
class GlobalPrivacy;
|
||||
@@ -121,9 +122,9 @@ public:
|
||||
static std::optional<Key> KeyFromMTP(mtpTypeId type);
|
||||
};
|
||||
|
||||
struct BlockedUsersSlice {
|
||||
struct BlockedPeersSlice {
|
||||
struct Item {
|
||||
UserData *user = nullptr;
|
||||
PeerData *peer = nullptr;
|
||||
TimeId date = 0;
|
||||
|
||||
bool operator==(const Item &other) const;
|
||||
@@ -133,8 +134,8 @@ public:
|
||||
QVector<Item> list;
|
||||
int total = 0;
|
||||
|
||||
bool operator==(const BlockedUsersSlice &other) const;
|
||||
bool operator!=(const BlockedUsersSlice &other) const;
|
||||
bool operator==(const BlockedPeersSlice &other) const;
|
||||
bool operator!=(const BlockedPeersSlice &other) const;
|
||||
};
|
||||
|
||||
explicit ApiWrap(not_null<Main::Session*> session);
|
||||
@@ -172,7 +173,9 @@ public:
|
||||
ChannelData *channel,
|
||||
MsgId msgId,
|
||||
RequestMessageDataCallback callback);
|
||||
QString exportDirectMessageLink(not_null<HistoryItem*> item);
|
||||
QString exportDirectMessageLink(
|
||||
not_null<HistoryItem*> item,
|
||||
bool inRepliesContext);
|
||||
|
||||
void requestContacts();
|
||||
void requestDialogs(Data::Folder *folder = nullptr);
|
||||
@@ -275,20 +278,12 @@ public:
|
||||
const MTPInputStickerSet &set);
|
||||
std::vector<not_null<DocumentData*>> *stickersByEmoji(
|
||||
not_null<EmojiPtr> emoji);
|
||||
void toggleFavedSticker(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool faved);
|
||||
void toggleSavedGif(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
bool saved);
|
||||
|
||||
void joinChannel(not_null<ChannelData*> channel);
|
||||
void leaveChannel(not_null<ChannelData*> channel);
|
||||
|
||||
void blockUser(not_null<UserData*> user);
|
||||
void unblockUser(not_null<UserData*> user, Fn<void()> onDone = nullptr);
|
||||
void blockPeer(not_null<PeerData*> peer);
|
||||
void unblockPeer(not_null<PeerData*> peer, Fn<void()> onDone = nullptr);
|
||||
|
||||
void exportInviteLink(not_null<PeerData*> peer);
|
||||
void requestNotifySettings(const MTPInputNotifyPeer &peer);
|
||||
@@ -456,9 +451,10 @@ public:
|
||||
void reloadPrivacy(Privacy::Key key);
|
||||
rpl::producer<Privacy> privacyValue(Privacy::Key key);
|
||||
|
||||
void reloadBlockedUsers();
|
||||
rpl::producer<BlockedUsersSlice> blockedUsersSlice();
|
||||
void reloadBlockedPeers();
|
||||
rpl::producer<BlockedPeersSlice> blockedPeersSlice();
|
||||
|
||||
[[nodiscard]] Api::Authorizations &authorizations();
|
||||
[[nodiscard]] Api::SelfDestruct &selfDestruct();
|
||||
[[nodiscard]] Api::SensitiveContent &sensitiveContent();
|
||||
[[nodiscard]] Api::GlobalPrivacy &globalPrivacy();
|
||||
@@ -698,7 +694,7 @@ private:
|
||||
QMap<uint64, QPair<uint64, mtpRequestId> > _stickerSetRequests;
|
||||
|
||||
QMap<ChannelData*, mtpRequestId> _channelAmInRequests;
|
||||
base::flat_map<not_null<UserData*>, mtpRequestId> _blockRequests;
|
||||
base::flat_map<not_null<PeerData*>, mtpRequestId> _blockRequests;
|
||||
base::flat_map<not_null<PeerData*>, mtpRequestId> _exportInviteRequests;
|
||||
base::flat_map<PeerId, mtpRequestId> _notifySettingRequests;
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _draftsSaveRequestIds;
|
||||
@@ -817,10 +813,11 @@ private:
|
||||
base::flat_map<Privacy::Key, Privacy> _privacyValues;
|
||||
std::map<Privacy::Key, rpl::event_stream<Privacy>> _privacyChanges;
|
||||
|
||||
mtpRequestId _blockedUsersRequestId = 0;
|
||||
std::optional<BlockedUsersSlice> _blockedUsersSlice;
|
||||
rpl::event_stream<BlockedUsersSlice> _blockedUsersChanges;
|
||||
mtpRequestId _blockedPeersRequestId = 0;
|
||||
std::optional<BlockedPeersSlice> _blockedPeersSlice;
|
||||
rpl::event_stream<BlockedPeersSlice> _blockedPeersChanges;
|
||||
|
||||
const std::unique_ptr<Api::Authorizations> _authorizations;
|
||||
const std::unique_ptr<Api::SelfDestruct> _selfDestruct;
|
||||
const std::unique_ptr<Api::SensitiveContent> _sensitiveContent;
|
||||
const std::unique_ptr<Api::GlobalPrivacy> _globalPrivacy;
|
||||
|
||||
@@ -125,7 +125,7 @@ QString telegramFaqLink() {
|
||||
const auto langpacked = [&](const char *language) {
|
||||
return result + '/' + language;
|
||||
};
|
||||
const auto current = Lang::Current().id();
|
||||
const auto current = Lang::Id();
|
||||
for (const auto language : { "de", "es", "it", "ko" }) {
|
||||
if (current.startsWith(QLatin1String(language))) {
|
||||
return langpacked(language);
|
||||
|
||||
@@ -244,7 +244,6 @@ private:
|
||||
|
||||
Fn<void()> _revokeCallback;
|
||||
mtpRequestId _revokeRequestId = 0;
|
||||
QPointer<ConfirmBox> _weakRevokeConfirmBox;
|
||||
|
||||
};
|
||||
|
||||
@@ -1417,21 +1416,21 @@ void RevokePublicLinkBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
lt_group,
|
||||
pressed->name);
|
||||
auto confirmText = tr::lng_channels_too_much_public_revoke(tr::now);
|
||||
_weakRevokeConfirmBox = Ui::show(Box<ConfirmBox>(text, confirmText, crl::guard(this, [this, pressed]() {
|
||||
auto callback = crl::guard(this, [=](Fn<void()> &&close) {
|
||||
if (_revokeRequestId) return;
|
||||
_revokeRequestId = _api.request(MTPchannels_UpdateUsername(
|
||||
pressed->asChannel()->inputChannel,
|
||||
MTP_string()
|
||||
)).done([=](const MTPBool &result) {
|
||||
const auto callback = _revokeCallback;
|
||||
if (_weakRevokeConfirmBox) {
|
||||
_weakRevokeConfirmBox->closeBox();
|
||||
}
|
||||
if (callback) {
|
||||
)).done([=, close = std::move(close)](const MTPBool &result) {
|
||||
close();
|
||||
if (const auto callback = _revokeCallback) {
|
||||
callback();
|
||||
}
|
||||
}).send();
|
||||
})), Ui::LayerOption::KeepOther);
|
||||
});
|
||||
Ui::show(
|
||||
Box<ConfirmBox>(text, confirmText, std::move(callback)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ private:
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
using Selection = base::optional_variant<Selected, DeleteSelected>;
|
||||
using Selection = std::variant<v::null_t, Selected, DeleteSelected>;
|
||||
|
||||
int getSelectionIndex(const Selection &selection) const;
|
||||
void repaintPaper(int index);
|
||||
@@ -163,12 +163,9 @@ void BackgroundBox::prepare() {
|
||||
}
|
||||
|
||||
void BackgroundBox::removePaper(const Data::WallPaper &paper) {
|
||||
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||
const auto session = &_controller->session();
|
||||
const auto remove = [=, weak = Ui::MakeWeak(this)]{
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
const auto remove = [=, weak = Ui::MakeWeak(this)](Fn<void()> &&close) {
|
||||
close();
|
||||
if (weak) {
|
||||
weak->_inner->removePaper(paper);
|
||||
}
|
||||
@@ -179,7 +176,7 @@ void BackgroundBox::removePaper(const Data::WallPaper &paper) {
|
||||
paper.mtpSettings()
|
||||
)).send();
|
||||
};
|
||||
*box = Ui::show(
|
||||
Ui::show(
|
||||
Box<ConfirmBox>(
|
||||
tr::lng_background_sure_delete(tr::now),
|
||||
tr::lng_selected_delete(tr::now),
|
||||
@@ -358,16 +355,16 @@ void BackgroundBox::Inner::paintPaper(
|
||||
p.drawPixmap(x, y, paper.thumbnail);
|
||||
}
|
||||
|
||||
const auto over = _overDown ? _overDown : _over;
|
||||
const auto over = !v::is_null(_overDown) ? _overDown : _over;
|
||||
if (paper.data.id() == Window::Theme::Background()->id()) {
|
||||
const auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
const auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
_check->paint(p, checkLeft, checkTop, width());
|
||||
} else if (Data::IsCloudWallPaper(paper.data)
|
||||
&& !Data::IsDefaultWallPaper(paper.data)
|
||||
&& over.has_value()
|
||||
&& !v::is_null(over)
|
||||
&& (&paper == &_papers[getSelectionIndex(over)])) {
|
||||
const auto deleteSelected = over.is<DeleteSelected>();
|
||||
const auto deleteSelected = v::is<DeleteSelected>(over);
|
||||
const auto deletePos = QPoint(x + st::backgroundSize.width() - st::stickerPanDeleteIconBg.width(), y);
|
||||
p.setOpacity(deleteSelected ? st::stickerPanDeleteOpacityBgOver : st::stickerPanDeleteOpacityBg);
|
||||
st::stickerPanDeleteIconBg.paint(p, deletePos, width());
|
||||
@@ -414,7 +411,7 @@ void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) {
|
||||
repaintPaper(getSelectionIndex(_over));
|
||||
_over = newOver;
|
||||
repaintPaper(getSelectionIndex(_over));
|
||||
setCursor((_over.has_value() || _overDown.has_value())
|
||||
setCursor((!v::is_null(_over) || !v::is_null(_overDown))
|
||||
? style::cur_pointer
|
||||
: style::cur_default);
|
||||
}
|
||||
@@ -442,22 +439,22 @@ void BackgroundBox::Inner::mousePressEvent(QMouseEvent *e) {
|
||||
|
||||
int BackgroundBox::Inner::getSelectionIndex(
|
||||
const Selection &selection) const {
|
||||
return selection.match([](const Selected &data) {
|
||||
return v::match(selection, [](const Selected &data) {
|
||||
return data.index;
|
||||
}, [](const DeleteSelected &data) {
|
||||
return data.index;
|
||||
}, [](std::nullopt_t) {
|
||||
}, [](v::null_t) {
|
||||
return -1;
|
||||
});
|
||||
}
|
||||
|
||||
void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (base::take(_overDown) == _over && _over.has_value()) {
|
||||
if (base::take(_overDown) == _over && !v::is_null(_over)) {
|
||||
const auto index = getSelectionIndex(_over);
|
||||
if (index >= 0 && index < _papers.size()) {
|
||||
if (base::get_if<DeleteSelected>(&_over)) {
|
||||
if (std::get_if<DeleteSelected>(&_over)) {
|
||||
_backgroundRemove.fire_copy(_papers[index].data);
|
||||
} else if (base::get_if<Selected>(&_over)) {
|
||||
} else if (std::get_if<Selected>(&_over)) {
|
||||
auto &paper = _papers[index];
|
||||
if (!paper.dataMedia) {
|
||||
if (const auto document = paper.data.document()) {
|
||||
@@ -468,7 +465,7 @@ void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
_backgroundChosen.fire_copy(paper.data);
|
||||
}
|
||||
}
|
||||
} else if (!_over.has_value()) {
|
||||
} else if (v::is_null(_over)) {
|
||||
setCursor(style::cur_default);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,6 +348,7 @@ sessionInfoFont: msgFont;
|
||||
sessionInfoFg: windowSubTextFg;
|
||||
sessionTerminateTop: 28px;
|
||||
sessionTerminateSkip: 22px;
|
||||
sessionNamePadding: margins(0px, 0px, 5px, 0px);
|
||||
sessionTerminate: IconButton {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
@@ -366,6 +367,15 @@ sessionTerminateAllButton: LinkButton(boxLinkButton) {
|
||||
color: attentionButtonFg;
|
||||
overColor: attentionButtonFg;
|
||||
}
|
||||
sessionNameStyle: TextStyle(defaultTextStyle) {
|
||||
font: sessionNameFont;
|
||||
}
|
||||
sessionWhenStyle: TextStyle(defaultTextStyle) {
|
||||
font: sessionWhenFont;
|
||||
}
|
||||
sessionInfoStyle: TextStyle(defaultTextStyle) {
|
||||
font: sessionInfoFont;
|
||||
}
|
||||
|
||||
passcodeHeaderFont: font(19px);
|
||||
passcodeHeaderHeight: 80px;
|
||||
|
||||
@@ -74,7 +74,7 @@ TextParseOptions kMarkedTextBoxOptions = {
|
||||
ConfirmBox::ConfirmBox(
|
||||
QWidget*,
|
||||
const QString &text,
|
||||
FnMut<void()> confirmedCallback,
|
||||
ConfirmBox::ConfirmedCallback confirmedCallback,
|
||||
FnMut<void()> cancelledCallback)
|
||||
: _confirmText(tr::lng_box_ok(tr::now))
|
||||
, _cancelText(tr::lng_cancel(tr::now))
|
||||
@@ -89,7 +89,7 @@ ConfirmBox::ConfirmBox(
|
||||
QWidget*,
|
||||
const QString &text,
|
||||
const QString &confirmText,
|
||||
FnMut<void()> confirmedCallback,
|
||||
ConfirmBox::ConfirmedCallback confirmedCallback,
|
||||
FnMut<void()> cancelledCallback)
|
||||
: _confirmText(confirmText)
|
||||
, _cancelText(tr::lng_cancel(tr::now))
|
||||
@@ -104,7 +104,7 @@ ConfirmBox::ConfirmBox(
|
||||
QWidget*,
|
||||
const TextWithEntities &text,
|
||||
const QString &confirmText,
|
||||
FnMut<void()> confirmedCallback,
|
||||
ConfirmBox::ConfirmedCallback confirmedCallback,
|
||||
FnMut<void()> cancelledCallback)
|
||||
: _confirmText(confirmText)
|
||||
, _cancelText(tr::lng_cancel(tr::now))
|
||||
@@ -120,7 +120,7 @@ ConfirmBox::ConfirmBox(
|
||||
const QString &text,
|
||||
const QString &confirmText,
|
||||
const style::RoundButton &confirmStyle,
|
||||
FnMut<void()> confirmedCallback,
|
||||
ConfirmBox::ConfirmedCallback confirmedCallback,
|
||||
FnMut<void()> cancelledCallback)
|
||||
: _confirmText(confirmText)
|
||||
, _cancelText(tr::lng_cancel(tr::now))
|
||||
@@ -136,7 +136,7 @@ ConfirmBox::ConfirmBox(
|
||||
const QString &text,
|
||||
const QString &confirmText,
|
||||
const QString &cancelText,
|
||||
FnMut<void()> confirmedCallback,
|
||||
ConfirmBox::ConfirmedCallback confirmedCallback,
|
||||
FnMut<void()> cancelledCallback)
|
||||
: _confirmText(confirmText)
|
||||
, _cancelText(cancelText)
|
||||
@@ -153,7 +153,7 @@ ConfirmBox::ConfirmBox(
|
||||
const QString &confirmText,
|
||||
const style::RoundButton &confirmStyle,
|
||||
const QString &cancelText,
|
||||
FnMut<void()> confirmedCallback,
|
||||
ConfirmBox::ConfirmedCallback confirmedCallback,
|
||||
FnMut<void()> cancelledCallback)
|
||||
: _confirmText(confirmText)
|
||||
, _cancelText(cancelText)
|
||||
@@ -256,8 +256,16 @@ void ConfirmBox::textUpdated() {
|
||||
void ConfirmBox::confirmed() {
|
||||
if (!_confirmed) {
|
||||
_confirmed = true;
|
||||
if (auto callback = std::move(_confirmedCallback)) {
|
||||
callback();
|
||||
|
||||
const auto confirmed = &_confirmedCallback;
|
||||
if (const auto callbackPtr = std::get_if<1>(confirmed)) {
|
||||
if (auto callback = base::take(*callbackPtr)) {
|
||||
callback();
|
||||
}
|
||||
} else if (const auto callbackPtr = std::get_if<2>(confirmed)) {
|
||||
if (auto callback = base::take(*callbackPtr)) {
|
||||
callback([=] { closeBox(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -808,53 +816,7 @@ void DeleteMessagesBox::deleteAndClear() {
|
||||
_deleteConfirmedCallback();
|
||||
}
|
||||
|
||||
auto remove = std::vector<not_null<HistoryItem*>>();
|
||||
remove.reserve(_ids.size());
|
||||
base::flat_map<not_null<History*>, QVector<MTPint>> idsByPeer;
|
||||
base::flat_map<not_null<PeerData*>, QVector<MTPint>> scheduledIdsByPeer;
|
||||
for (const auto itemId : _ids) {
|
||||
if (const auto item = _session->data().message(itemId)) {
|
||||
const auto history = item->history();
|
||||
if (item->isScheduled()) {
|
||||
const auto wasOnServer = !item->isSending()
|
||||
&& !item->hasFailed();
|
||||
if (wasOnServer) {
|
||||
scheduledIdsByPeer[history->peer].push_back(MTP_int(
|
||||
_session->data().scheduledMessages().lookupId(item)));
|
||||
} else {
|
||||
_session->data().scheduledMessages().removeSending(item);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
remove.push_back(item);
|
||||
if (IsServerMsgId(item->id)) {
|
||||
idsByPeer[history].push_back(MTP_int(itemId.msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &[history, ids] : idsByPeer) {
|
||||
history->owner().histories().deleteMessages(history, ids, revoke);
|
||||
}
|
||||
for (const auto &[peer, ids] : scheduledIdsByPeer) {
|
||||
peer->session().api().request(MTPmessages_DeleteScheduledMessages(
|
||||
peer->input,
|
||||
MTP_vector<MTPint>(ids)
|
||||
)).done([peer=peer](const MTPUpdates &result) {
|
||||
peer->session().api().applyUpdates(result);
|
||||
}).send();
|
||||
}
|
||||
|
||||
for (const auto item : remove) {
|
||||
const auto history = item->history();
|
||||
const auto wasLast = (history->lastMessage() == item);
|
||||
const auto wasInChats = (history->chatListMessage() == item);
|
||||
item->destroy();
|
||||
|
||||
if (wasLast || wasInChats) {
|
||||
history->requestChatListMessage();
|
||||
}
|
||||
}
|
||||
_session->data().histories().deleteMessages(_ids, revoke);
|
||||
|
||||
const auto session = _session;
|
||||
Ui::hideLayer();
|
||||
|
||||
@@ -28,12 +28,51 @@ class EmptyUserpic;
|
||||
class InformBox;
|
||||
class ConfirmBox : public Ui::BoxContent, public ClickHandlerHost {
|
||||
public:
|
||||
ConfirmBox(QWidget*, const QString &text, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const QString &cancelText, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, const QString &cancelText, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(QWidget*, const TextWithEntities &text, const QString &confirmText, FnMut<void()> confirmedCallback = nullptr, FnMut<void()> cancelledCallback = nullptr);
|
||||
|
||||
using ConfirmedCallback = std::variant<
|
||||
v::null_t,
|
||||
FnMut<void()>,
|
||||
FnMut<void(Fn<void()>)>>;
|
||||
|
||||
ConfirmBox(
|
||||
QWidget*,
|
||||
const QString &text,
|
||||
ConfirmedCallback confirmedCallback = FnMut<void()>(),
|
||||
FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(
|
||||
QWidget*,
|
||||
const QString &text,
|
||||
const QString &confirmText,
|
||||
ConfirmedCallback confirmedCallback = FnMut<void()>(),
|
||||
FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(
|
||||
QWidget*,
|
||||
const QString &text,
|
||||
const QString &confirmText,
|
||||
const style::RoundButton &confirmStyle,
|
||||
ConfirmedCallback confirmedCallback = FnMut<void()>(),
|
||||
FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(
|
||||
QWidget*,
|
||||
const QString &text,
|
||||
const QString &confirmText,
|
||||
const QString &cancelText,
|
||||
ConfirmedCallback confirmedCallback = FnMut<void()>(),
|
||||
FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(
|
||||
QWidget*,
|
||||
const QString &text,
|
||||
const QString &confirmText,
|
||||
const style::RoundButton &confirmStyle,
|
||||
const QString &cancelText,
|
||||
ConfirmedCallback confirmedCallback = FnMut<void()>(),
|
||||
FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(
|
||||
QWidget*,
|
||||
const TextWithEntities &text,
|
||||
const QString &confirmText,
|
||||
ConfirmedCallback confirmedCallback = v::null,
|
||||
FnMut<void()> cancelledCallback = nullptr);
|
||||
|
||||
void updateLink();
|
||||
|
||||
@@ -87,7 +126,7 @@ private:
|
||||
bool _confirmed = false;
|
||||
bool _cancelled = false;
|
||||
bool _strictCancel = false;
|
||||
FnMut<void()> _confirmedCallback;
|
||||
ConfirmBox::ConfirmedCallback _confirmedCallback;
|
||||
FnMut<void()> _cancelledCallback;
|
||||
|
||||
};
|
||||
|
||||
@@ -41,6 +41,48 @@ constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000);
|
||||
|
||||
using ProxyData = MTP::ProxyData;
|
||||
|
||||
class HostInput : public Ui::MaskedInputField {
|
||||
public:
|
||||
HostInput(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
rpl::producer<QString> placeholder,
|
||||
const QString &val);
|
||||
|
||||
protected:
|
||||
void correctValue(
|
||||
const QString &was,
|
||||
int wasCursor,
|
||||
QString &now,
|
||||
int &nowCursor) override;
|
||||
};
|
||||
|
||||
HostInput::HostInput(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
rpl::producer<QString> placeholder,
|
||||
const QString &val)
|
||||
: MaskedInputField(parent, st, std::move(placeholder), val) {
|
||||
}
|
||||
|
||||
void HostInput::correctValue(
|
||||
const QString &was,
|
||||
int wasCursor,
|
||||
QString &now,
|
||||
int &nowCursor) {
|
||||
QString newText;
|
||||
int newCursor = nowCursor;
|
||||
newText.reserve(now.size());
|
||||
for (auto i = 0, l = now.size(); i < l; ++i) {
|
||||
if (now[i] == ',') {
|
||||
newText.append('.');
|
||||
} else {
|
||||
newText.append(now[i]);
|
||||
}
|
||||
}
|
||||
setCorrectedText(now, nowCursor, newText, newCursor);
|
||||
}
|
||||
|
||||
class Base64UrlInput : public Ui::MaskedInputField {
|
||||
public:
|
||||
Base64UrlInput(
|
||||
@@ -209,7 +251,7 @@ private:
|
||||
std::shared_ptr<Ui::RadioenumGroup<Type>> _type;
|
||||
|
||||
QPointer<Ui::SlideWrap<>> _aboutSponsored;
|
||||
QPointer<Ui::InputField> _host;
|
||||
QPointer<HostInput> _host;
|
||||
QPointer<Ui::PortInput> _port;
|
||||
QPointer<Ui::InputField> _user;
|
||||
QPointer<Ui::PasswordInput> _password;
|
||||
@@ -767,12 +809,12 @@ ProxyBox::ProxyBox(
|
||||
void ProxyBox::prepare() {
|
||||
setTitle(tr::lng_proxy_edit());
|
||||
|
||||
connect(_host.data(), &Ui::InputField::changed, [=] {
|
||||
connect(_host.data(), &HostInput::changed, [=] {
|
||||
Ui::PostponeCall(_host, [=] {
|
||||
const auto host = _host->getLastText().trimmed();
|
||||
static const auto mask = u"^\\d+\\.\\d+\\.\\d+\\.\\d+:(\\d*)$"_q;
|
||||
const auto match = QRegularExpression(mask).match(host);
|
||||
if (_host->textCursor().position() == host.size()
|
||||
if (_host->cursorPosition() == host.size()
|
||||
&& match.hasMatch()) {
|
||||
const auto port = match.captured(1);
|
||||
_port->setText(port);
|
||||
@@ -881,7 +923,7 @@ void ProxyBox::setupSocketAddress(const ProxyData &data) {
|
||||
_content,
|
||||
st::connectionHostInputField.heightMin),
|
||||
st::proxyEditInputPadding);
|
||||
_host = Ui::CreateChild<Ui::InputField>(
|
||||
_host = Ui::CreateChild<HostInput>(
|
||||
address,
|
||||
st::connectionHostInputField,
|
||||
tr::lng_connection_host_ph(),
|
||||
@@ -1043,7 +1085,6 @@ void ProxiesBoxController::ShowApplyConfirmation(
|
||||
proxy.password = fields.value(qsl("secret"));
|
||||
}
|
||||
if (proxy) {
|
||||
const auto box = std::make_shared<QPointer<ConfirmBox>>();
|
||||
const auto text = tr::lng_sure_enable_socks(
|
||||
tr::now,
|
||||
lt_server,
|
||||
@@ -1053,7 +1094,7 @@ void ProxiesBoxController::ShowApplyConfirmation(
|
||||
+ (proxy.type == Type::Mtproto
|
||||
? "\n\n" + tr::lng_proxy_sponsor_warning(tr::now)
|
||||
: QString());
|
||||
*box = Ui::show(Box<ConfirmBox>(text, tr::lng_sure_enable(tr::now), [=] {
|
||||
auto callback = [=](Fn<void()> &&close) {
|
||||
auto &proxies = Global::RefProxiesList();
|
||||
if (!ranges::contains(proxies, proxy)) {
|
||||
proxies.push_back(proxy);
|
||||
@@ -1062,10 +1103,14 @@ void ProxiesBoxController::ShowApplyConfirmation(
|
||||
proxy,
|
||||
ProxyData::Settings::Enabled);
|
||||
Local::writeSettings();
|
||||
if (const auto strong = box->data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
}), Ui::LayerOption::KeepOther);
|
||||
close();
|
||||
};
|
||||
Ui::show(
|
||||
Box<ConfirmBox>(
|
||||
text,
|
||||
tr::lng_sure_enable(tr::now),
|
||||
std::move(callback)),
|
||||
Ui::LayerOption::KeepOther);
|
||||
} else {
|
||||
Ui::show(Box<InformBox>(
|
||||
(proxy.status() == ProxyData::Status::Unsupported
|
||||
|
||||
@@ -225,7 +225,7 @@ auto AddButtonWithLoader(
|
||||
|
||||
buttonState->value(
|
||||
) | rpl::start_with_next([=](const DictState &state) {
|
||||
const auto isToggledSet = state.is<Active>();
|
||||
const auto isToggledSet = v::is<Active>(state);
|
||||
const auto toggled = isToggledSet ? 1. : 0.;
|
||||
const auto over = !button->isDisabled()
|
||||
&& (button->isDown() || button->isOver());
|
||||
@@ -252,7 +252,7 @@ auto AddButtonWithLoader(
|
||||
dictionaryRemoved->events(),
|
||||
buttonState->value(
|
||||
) | rpl::filter([](const DictState &state) {
|
||||
return state.is<Failed>();
|
||||
return v::is<Failed>(state);
|
||||
}) | rpl::to_empty
|
||||
) | rpl::map_to(false)
|
||||
)
|
||||
@@ -276,13 +276,14 @@ auto AddButtonWithLoader(
|
||||
});
|
||||
}) | rpl::flatten_latest(
|
||||
) | rpl::filter([=](const DictState &state) {
|
||||
return !buttonState->current().is<Failed>() || !state.is<Available>();
|
||||
return !v::is<Failed>(buttonState->current())
|
||||
|| !v::is<Available>(state);
|
||||
});
|
||||
|
||||
button->toggledValue(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
const auto &state = buttonState->current();
|
||||
if (toggled && (state.is<Available>() || state.is<Failed>())) {
|
||||
if (toggled && (v::is<Available>(state) || v::is<Failed>(state))) {
|
||||
const auto weak = Ui::MakeWeak(button);
|
||||
setLocalLoader(base::make_unique_q<Loader>(
|
||||
QCoreApplication::instance(),
|
||||
@@ -292,7 +293,7 @@ auto AddButtonWithLoader(
|
||||
Spellchecker::DictPathByLangId(id),
|
||||
Spellchecker::GetDownloadSize(id),
|
||||
crl::guard(weak, destroyLocalLoader)));
|
||||
} else if (!toggled && state.is<Loading>()) {
|
||||
} else if (!toggled && v::is<Loading>(state)) {
|
||||
if (const auto g = rawGlobalLoaderPtr()) {
|
||||
g->destroy();
|
||||
return;
|
||||
|
||||
@@ -34,7 +34,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "layout.h"
|
||||
#include "media/streaming/media_streaming_instance.h"
|
||||
#include "media/streaming/media_streaming_player.h"
|
||||
#include "media/streaming/media_streaming_document.h"
|
||||
@@ -47,6 +46,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/text_options.h"
|
||||
#include "window/window_session_controller.h"
|
||||
@@ -431,7 +431,7 @@ void EditCaptionBox::setupStreamedPreview(std::shared_ptr<Document> shared) {
|
||||
}
|
||||
|
||||
void EditCaptionBox::handleStreamingUpdate(Update &&update) {
|
||||
update.data.match([&](Information &update) {
|
||||
v::match(update.data, [&](Information &update) {
|
||||
streamingReady(std::move(update));
|
||||
}, [&](const PreloadedVideo &update) {
|
||||
}, [&](const UpdateVideo &update) {
|
||||
@@ -489,7 +489,7 @@ void EditCaptionBox::updateEditPreview() {
|
||||
auto isGif = false;
|
||||
auto shouldAsDoc = true;
|
||||
auto docPhotoSize = QSize();
|
||||
if (const auto image = base::get_if<Info::Image>(fileMedia)) {
|
||||
if (const auto image = std::get_if<Info::Image>(fileMedia)) {
|
||||
shouldAsDoc = !Storage::ValidateThumbDimensions(
|
||||
image->data.width(),
|
||||
image->data.height());
|
||||
@@ -501,14 +501,14 @@ void EditCaptionBox::updateEditPreview() {
|
||||
_animated = isGif;
|
||||
_photo = !isGif && !shouldAsDoc;
|
||||
_isImage = true;
|
||||
} else if (const auto video = base::get_if<Info::Video>(fileMedia)) {
|
||||
} else if (const auto video = std::get_if<Info::Video>(fileMedia)) {
|
||||
isGif = video->isGifv;
|
||||
_animated = true;
|
||||
shouldAsDoc = false;
|
||||
}
|
||||
if (shouldAsDoc) {
|
||||
auto nameString = filename;
|
||||
if (const auto song = base::get_if<Info::Song>(fileMedia)) {
|
||||
if (const auto song = std::get_if<Info::Song>(fileMedia)) {
|
||||
nameString = DocumentData::ComposeNameString(
|
||||
filename,
|
||||
song->title,
|
||||
@@ -684,7 +684,7 @@ bool EditCaptionBox::fileFromClipboard(not_null<const QMimeData*> data) {
|
||||
const auto imageAsDoc = [&] {
|
||||
using Info = FileMediaInformation;
|
||||
const auto fileMedia = &file->information->media;
|
||||
if (const auto image = base::get_if<Info::Image>(fileMedia)) {
|
||||
if (const auto image = std::get_if<Info::Image>(fileMedia)) {
|
||||
return !Storage::ValidateThumbDimensions(
|
||||
image->data.width(),
|
||||
image->data.height());
|
||||
@@ -1030,7 +1030,7 @@ void EditCaptionBox::setName(QString nameString, qint64 size) {
|
||||
st::semiboldTextStyle,
|
||||
nameString,
|
||||
Ui::NameTextOptions());
|
||||
_status = formatSizeText(size);
|
||||
_status = Ui::FormatSizeText(size);
|
||||
}
|
||||
|
||||
void EditCaptionBox::keyPressEvent(QKeyEvent *e) {
|
||||
|
||||
@@ -86,7 +86,7 @@ void PrivacyExceptionsBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
}
|
||||
|
||||
std::unique_ptr<PrivacyExceptionsBoxController::Row> PrivacyExceptionsBoxController::createRow(not_null<History*> history) {
|
||||
if (history->peer->isSelf()) {
|
||||
if (history->peer->isSelf() || history->peer->isRepliesChat()) {
|
||||
return nullptr;
|
||||
} else if (!history->peer->isUser()
|
||||
&& !history->peer->isChat()
|
||||
|
||||
@@ -241,19 +241,31 @@ void FilterChatsPreview::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
for (auto &[history, userpic, button] : _removePeer) {
|
||||
const auto savedMessages = history->peer->isSelf();
|
||||
if (savedMessages) {
|
||||
Ui::EmptyUserpic::PaintSavedMessages(
|
||||
p,
|
||||
iconLeft,
|
||||
top + iconTop,
|
||||
width(),
|
||||
st.photoSize);
|
||||
const auto repliesMessages = history->peer->isRepliesChat();
|
||||
if (savedMessages || repliesMessages) {
|
||||
if (savedMessages) {
|
||||
Ui::EmptyUserpic::PaintSavedMessages(
|
||||
p,
|
||||
iconLeft,
|
||||
top + iconTop,
|
||||
width(),
|
||||
st.photoSize);
|
||||
} else {
|
||||
Ui::EmptyUserpic::PaintRepliesMessages(
|
||||
p,
|
||||
iconLeft,
|
||||
top + iconTop,
|
||||
width(),
|
||||
st.photoSize);
|
||||
}
|
||||
p.setPen(st::contactsNameFg);
|
||||
p.drawTextLeft(
|
||||
nameLeft,
|
||||
top + nameTop,
|
||||
width(),
|
||||
tr::lng_saved_messages(tr::now));
|
||||
(savedMessages
|
||||
? tr::lng_saved_messages(tr::now)
|
||||
: tr::lng_replies_messages(tr::now)));
|
||||
} else {
|
||||
history->peer->paintUserpicLeft(
|
||||
p,
|
||||
|
||||
@@ -170,6 +170,8 @@ ExceptionRow::ExceptionRow(not_null<History*> history) : Row(history) {
|
||||
QString ExceptionRow::generateName() {
|
||||
return peer()->isSelf()
|
||||
? tr::lng_saved_messages(tr::now)
|
||||
: peer()->isRepliesChat()
|
||||
? tr::lng_replies_messages(tr::now)
|
||||
: Row::generateName();
|
||||
}
|
||||
|
||||
@@ -180,10 +182,13 @@ QString ExceptionRow::generateShortName() {
|
||||
PaintRoundImageCallback ExceptionRow::generatePaintUserpicCallback() {
|
||||
const auto peer = this->peer();
|
||||
const auto saved = peer->isSelf();
|
||||
const auto replies = peer->isRepliesChat();
|
||||
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 if (replies) {
|
||||
Ui::EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
|
||||
} else {
|
||||
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ private:
|
||||
return (index == other.index);
|
||||
}
|
||||
};
|
||||
using Selection = base::optional_variant<RowSelection, MenuSelection>;
|
||||
using Selection = std::variant<v::null_t, RowSelection, MenuSelection>;
|
||||
|
||||
void updateSelected(Selection selected);
|
||||
void updatePressed(Selection pressed);
|
||||
@@ -196,7 +196,7 @@ std::pair<Languages, Languages> PrepareLists() {
|
||||
const auto projId = [](const Language &language) {
|
||||
return language.id;
|
||||
};
|
||||
const auto current = Lang::LanguageIdOrDefault(Lang::Current().id());
|
||||
const auto current = Lang::LanguageIdOrDefault(Lang::Id());
|
||||
auto official = Lang::CurrentCloudManager().languageList();
|
||||
auto recent = Local::readRecentLanguages();
|
||||
ranges::stable_partition(recent, [&](const Language &language) {
|
||||
@@ -207,13 +207,13 @@ std::pair<Languages, Languages> PrepareLists() {
|
||||
const auto generate = [&] {
|
||||
const auto name = (current == "#custom")
|
||||
? "Custom lang pack"
|
||||
: Lang::Current().name();
|
||||
: Lang::GetInstance().name();
|
||||
return Language{
|
||||
current,
|
||||
QString(),
|
||||
QString(),
|
||||
name,
|
||||
Lang::Current().nativeName()
|
||||
Lang::GetInstance().nativeName()
|
||||
};
|
||||
};
|
||||
const auto i = ranges::find(official, current, projId);
|
||||
@@ -327,7 +327,7 @@ void Rows::mouseMoveEvent(QMouseEvent *e) {
|
||||
|
||||
void Rows::mousePressEvent(QMouseEvent *e) {
|
||||
updatePressed(_selected);
|
||||
if (_pressed.has_value()
|
||||
if (!v::is_null(_pressed)
|
||||
&& !rowBySelection(_pressed).menuToggleForceRippled) {
|
||||
addRipple(_pressed, e->pos());
|
||||
}
|
||||
@@ -348,11 +348,11 @@ QRect Rows::menuToggleArea(not_null<const Row*> row) const {
|
||||
}
|
||||
|
||||
void Rows::addRipple(Selection selected, QPoint position) {
|
||||
Expects(selected.has_value());
|
||||
Expects(!v::is_null(selected));
|
||||
|
||||
ensureRippleBySelection(selected);
|
||||
|
||||
const auto menu = selected.is<MenuSelection>();
|
||||
const auto menu = v::is<MenuSelection>(selected);
|
||||
const auto &row = rowBySelection(selected);
|
||||
const auto menuArea = menuToggleArea(&row);
|
||||
auto &ripple = rippleBySelection(&row, selected);
|
||||
@@ -369,7 +369,7 @@ void Rows::ensureRippleBySelection(not_null<Row*> row, Selection selected) {
|
||||
if (ripple) {
|
||||
return;
|
||||
}
|
||||
const auto menu = selected.is<MenuSelection>();
|
||||
const auto menu = v::is<MenuSelection>(selected);
|
||||
const auto menuArea = menuToggleArea(row);
|
||||
auto mask = menu
|
||||
? Ui::RippleAnimation::ellipseMask(menuArea.size())
|
||||
@@ -391,11 +391,11 @@ void Rows::mouseReleaseEvent(QMouseEvent *e) {
|
||||
const auto pressed = _pressed;
|
||||
updatePressed({});
|
||||
if (pressed == _selected) {
|
||||
pressed.match([&](RowSelection data) {
|
||||
v::match(pressed, [&](RowSelection data) {
|
||||
activateByIndex(data.index);
|
||||
}, [&](MenuSelection data) {
|
||||
showMenu(data.index);
|
||||
}, [](std::nullopt_t) {});
|
||||
}, [](v::null_t) {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,11 +597,11 @@ int Rows::count() const {
|
||||
}
|
||||
|
||||
int Rows::indexFromSelection(Selection selected) const {
|
||||
return selected.match([&](RowSelection data) {
|
||||
return v::match(selected, [&](RowSelection data) {
|
||||
return data.index;
|
||||
}, [&](MenuSelection data) {
|
||||
return data.index;
|
||||
}, [](std::nullopt_t) {
|
||||
}, [](v::null_t) {
|
||||
return -1;
|
||||
});
|
||||
}
|
||||
@@ -648,7 +648,7 @@ rpl::producer<bool> Rows::isEmpty() const {
|
||||
}
|
||||
|
||||
void Rows::repaint(Selection selected) {
|
||||
selected.match([](std::nullopt_t) {
|
||||
v::match(selected, [](v::null_t) {
|
||||
}, [&](const auto &data) {
|
||||
repaint(data.index);
|
||||
});
|
||||
@@ -672,17 +672,17 @@ void Rows::repaintChecked(not_null<const Row*> row) {
|
||||
}
|
||||
|
||||
void Rows::updateSelected(Selection selected) {
|
||||
const auto changed = (_selected.has_value() != selected.has_value());
|
||||
const auto changed = (v::is_null(_selected) != v::is_null(selected));
|
||||
repaint(_selected);
|
||||
_selected = selected;
|
||||
repaint(_selected);
|
||||
if (changed) {
|
||||
_hasSelection.fire(_selected.has_value());
|
||||
_hasSelection.fire(!v::is_null(_selected));
|
||||
}
|
||||
}
|
||||
|
||||
void Rows::updatePressed(Selection pressed) {
|
||||
if (_pressed.has_value()) {
|
||||
if (!v::is_null(_pressed)) {
|
||||
if (!rowBySelection(_pressed).menuToggleForceRippled) {
|
||||
if (const auto ripple = rippleBySelection(_pressed).get()) {
|
||||
ripple->lastStop();
|
||||
@@ -725,7 +725,7 @@ const std::unique_ptr<Ui::RippleAnimation> &Rows::rippleBySelection(
|
||||
std::unique_ptr<Ui::RippleAnimation> &Rows::rippleBySelection(
|
||||
not_null<Row*> row,
|
||||
Selection selected) {
|
||||
return selected.is<MenuSelection>()
|
||||
return v::is<MenuSelection>(selected)
|
||||
? row->menuToggleRipple
|
||||
: row->ripple;
|
||||
}
|
||||
@@ -796,7 +796,7 @@ void Rows::paintEvent(QPaintEvent *e) {
|
||||
const auto menu = menuToggleArea();
|
||||
const auto selectedIndex = (_menuShownIndex >= 0)
|
||||
? _menuShownIndex
|
||||
: indexFromSelection(_pressed.has_value() ? _pressed : _selected);
|
||||
: indexFromSelection(!v::is_null(_pressed) ? _pressed : _selected);
|
||||
for (auto i = 0, till = count(); i != till; ++i) {
|
||||
const auto &row = rowByIndex(i);
|
||||
if (row.top + row.height <= clip.y()) {
|
||||
@@ -867,7 +867,7 @@ void Content::setupContent(
|
||||
const Languages &official) {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
const auto current = Lang::LanguageIdOrDefault(Lang::Current().id());
|
||||
const auto current = Lang::LanguageIdOrDefault(Lang::Id());
|
||||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||
const auto add = [&](const Languages &list, bool areOfficial) {
|
||||
if (list.empty()) {
|
||||
@@ -1101,7 +1101,7 @@ void LanguageBox::prepare() {
|
||||
// "#custom" is applied each time it's passed to switchToLanguage().
|
||||
// So we check that the language really has changed.
|
||||
const auto currentId = [] {
|
||||
return Lang::LanguageIdOrDefault(Lang::Current().id());
|
||||
return Lang::LanguageIdOrDefault(Lang::Id());
|
||||
};
|
||||
if (language.id != currentId()) {
|
||||
Lang::CurrentCloudManager().switchToLanguage(language);
|
||||
|
||||
@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/emoji_config.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "storage/cache/storage_cache_database.h"
|
||||
@@ -21,7 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwindow.h"
|
||||
#include "main/main_session.h"
|
||||
#include "layout.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
@@ -260,7 +260,7 @@ QString LocalStorageBox::Row::titleText(const Database::TaggedSummary &data) con
|
||||
|
||||
QString LocalStorageBox::Row::sizeText(const Database::TaggedSummary &data) const {
|
||||
return data.totalSize
|
||||
? formatSizeText(data.totalSize)
|
||||
? Ui::FormatSizeText(data.totalSize)
|
||||
: tr::lng_local_storage_empty(tr::now);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_domain.h"
|
||||
#include "core/application.h"
|
||||
#include "storage/storage_domain.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
@@ -24,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "passport/passport_encryption.h"
|
||||
#include "passport/passport_panel_edit_contact.h"
|
||||
#include "settings/settings_privacy_security.h"
|
||||
#include "facades.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_passport.h"
|
||||
@@ -31,6 +34,68 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
enum class PasswordErrorType {
|
||||
None,
|
||||
NoPassword,
|
||||
Later,
|
||||
};
|
||||
|
||||
void SetCloudPassword(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session) {
|
||||
session->api().passwordState(
|
||||
) | rpl::start_with_next([=] {
|
||||
using namespace Settings;
|
||||
const auto weak = Ui::MakeWeak(box);
|
||||
if (CheckEditCloudPassword(session)) {
|
||||
box->getDelegate()->show(
|
||||
EditCloudPasswordBox(session));
|
||||
} else {
|
||||
box->getDelegate()->show(CloudPasswordAppOutdatedBox());
|
||||
}
|
||||
if (weak) {
|
||||
weak->closeBox();
|
||||
}
|
||||
}, box->lifetime());
|
||||
}
|
||||
|
||||
void TransferPasswordError(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session,
|
||||
TextWithEntities &&about,
|
||||
PasswordErrorType error) {
|
||||
box->setTitle(tr::lng_rights_transfer_check());
|
||||
box->setWidth(st::transferCheckWidth);
|
||||
|
||||
auto text = std::move(about).append('\n').append('\n').append(
|
||||
tr::lng_rights_transfer_check_password(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue)
|
||||
).append('\n').append('\n').append(
|
||||
tr::lng_rights_transfer_check_session(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue)
|
||||
);
|
||||
if (error == PasswordErrorType::Later) {
|
||||
text.append('\n').append('\n').append(
|
||||
tr::lng_rights_transfer_check_later(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue));
|
||||
}
|
||||
box->addRow(object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
rpl::single(text),
|
||||
st::boxLabel));
|
||||
if (error == PasswordErrorType::Later) {
|
||||
box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); });
|
||||
} else {
|
||||
box->addButton(tr::lng_rights_transfer_set_password(), [=] {
|
||||
SetCloudPassword(box, session);
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PasscodeBox::CloudFields PasscodeBox::CloudFields::From(
|
||||
@@ -537,14 +602,11 @@ void PasscodeBox::submitOnlyCheckCloudPassword(const QString &oldPassword) {
|
||||
if (_cloudFields.turningOff && _cloudFields.notEmptyPassport) {
|
||||
Assert(!_cloudFields.customCheckCallback);
|
||||
|
||||
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||
const auto confirmed = [=] {
|
||||
const auto confirmed = [=](Fn<void()> &&close) {
|
||||
send();
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
close();
|
||||
};
|
||||
*box = getDelegate()->show(Box<ConfirmBox>(
|
||||
getDelegate()->show(Box<ConfirmBox>(
|
||||
tr::lng_cloud_password_passport_losing(tr::now),
|
||||
tr::lng_continue(tr::now),
|
||||
confirmed));
|
||||
@@ -744,20 +806,16 @@ void PasscodeBox::changeCloudPassword(
|
||||
}
|
||||
|
||||
void PasscodeBox::suggestSecretReset(const QString &newPassword) {
|
||||
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||
const auto resetSecretAndSave = [=] {
|
||||
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
|
||||
resetSecret(check, newPassword, [=] {
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
});
|
||||
auto resetSecretAndSave = [=](Fn<void()> &&close) {
|
||||
checkPasswordHash([=, close = std::move(close)](
|
||||
const Core::CloudPasswordResult &check) {
|
||||
resetSecret(check, newPassword, std::move(close));
|
||||
});
|
||||
};
|
||||
*box = getDelegate()->show(Box<ConfirmBox>(
|
||||
getDelegate()->show(Box<ConfirmBox>(
|
||||
Lang::Hard::PassportCorruptedChange(),
|
||||
Lang::Hard::PassportCorruptedReset(),
|
||||
[=] { resetSecretAndSave(); }));
|
||||
std::move(resetSecretAndSave)));
|
||||
}
|
||||
|
||||
void PasscodeBox::resetSecret(
|
||||
@@ -1002,14 +1060,11 @@ void RecoverBox::submit() {
|
||||
}).handleFloodErrors().send();
|
||||
});
|
||||
if (_notEmptyPassport) {
|
||||
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||
const auto confirmed = [=] {
|
||||
const auto confirmed = [=](Fn<void()> &&close) {
|
||||
send();
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
close();
|
||||
};
|
||||
*box = getDelegate()->show(Box<ConfirmBox>(
|
||||
getDelegate()->show(Box<ConfirmBox>(
|
||||
tr::lng_cloud_password_passport_losing(tr::now),
|
||||
tr::lng_continue(tr::now),
|
||||
confirmed));
|
||||
@@ -1139,3 +1194,28 @@ RecoveryEmailValidation ConfirmRecoveryEmail(
|
||||
*weak = box.data();
|
||||
return { std::move(box), reloads->events(), cancels->events() };
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::GenericBox> PrePasswordErrorBox(
|
||||
const RPCError &error,
|
||||
not_null<Main::Session*> session,
|
||||
TextWithEntities &&about) {
|
||||
const auto type = [&] {
|
||||
const auto &type = error.type();
|
||||
if (type == qstr("PASSWORD_MISSING")) {
|
||||
return PasswordErrorType::NoPassword;
|
||||
} else if (type.startsWith(qstr("PASSWORD_TOO_FRESH_"))
|
||||
|| type.startsWith(qstr("SESSION_TOO_FRESH_"))) {
|
||||
return PasswordErrorType::Later;
|
||||
}
|
||||
return PasswordErrorType::None;
|
||||
}();
|
||||
if (type == PasswordErrorType::None) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Box(
|
||||
TransferPasswordError,
|
||||
session,
|
||||
std::move(about),
|
||||
type);
|
||||
}
|
||||
|
||||
@@ -214,3 +214,8 @@ struct RecoveryEmailValidation {
|
||||
[[nodiscard]] RecoveryEmailValidation ConfirmRecoveryEmail(
|
||||
not_null<Main::Session*> session,
|
||||
const QString &pattern);
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::GenericBox> PrePasswordErrorBox(
|
||||
const RPCError &error,
|
||||
not_null<Main::Session*> session,
|
||||
TextWithEntities &&about);
|
||||
|
||||
@@ -36,10 +36,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
PaintRoundImageCallback PaintUserpicCallback(
|
||||
not_null<PeerData*> peer,
|
||||
bool respectSavedMessagesChat) {
|
||||
if (respectSavedMessagesChat && peer->isSelf()) {
|
||||
return [](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
|
||||
};
|
||||
if (respectSavedMessagesChat) {
|
||||
if (peer->isSelf()) {
|
||||
return [](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
|
||||
};
|
||||
} else if (peer->isRepliesChat()) {
|
||||
return [](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
Ui::EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
|
||||
};
|
||||
}
|
||||
}
|
||||
auto userpic = std::shared_ptr<Data::CloudImageView>();
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
|
||||
@@ -318,6 +324,8 @@ void PeerListBox::addSelectItem(
|
||||
const auto respect = _controller->respectSavedMessagesChat();
|
||||
const auto text = (respect && peer->isSelf())
|
||||
? tr::lng_saved_short(tr::now)
|
||||
: (respect && peer->isRepliesChat())
|
||||
? tr::lng_replies_messages(tr::now)
|
||||
: peer->shortName();
|
||||
addSelectItem(
|
||||
peer->id,
|
||||
@@ -400,14 +408,16 @@ PeerListRow::PeerListRow(not_null<PeerData*> peer, PeerListRowId id)
|
||||
, _peer(peer)
|
||||
, _initialized(false)
|
||||
, _isSearchResult(false)
|
||||
, _isSavedMessagesChat(false) {
|
||||
, _isSavedMessagesChat(false)
|
||||
, _isRepliesMessagesChat(false) {
|
||||
}
|
||||
|
||||
PeerListRow::PeerListRow(PeerListRowId id)
|
||||
: _id(id)
|
||||
, _initialized(false)
|
||||
, _isSearchResult(false)
|
||||
, _isSavedMessagesChat(false) {
|
||||
, _isSavedMessagesChat(false)
|
||||
, _isRepliesMessagesChat(false) {
|
||||
}
|
||||
|
||||
PeerListRow::~PeerListRow() = default;
|
||||
@@ -470,6 +480,8 @@ void PeerListRow::refreshName(const style::PeerListItem &st) {
|
||||
}
|
||||
const auto text = _isSavedMessagesChat
|
||||
? tr::lng_saved_messages(tr::now)
|
||||
: _isRepliesMessagesChat
|
||||
? tr::lng_replies_messages(tr::now)
|
||||
: generateName();
|
||||
_name.setText(st.nameStyle, text, Ui::NameTextOptions());
|
||||
}
|
||||
@@ -481,6 +493,8 @@ QString PeerListRow::generateName() {
|
||||
QString PeerListRow::generateShortName() {
|
||||
return _isSavedMessagesChat
|
||||
? tr::lng_saved_short(tr::now)
|
||||
: _isRepliesMessagesChat
|
||||
? tr::lng_replies_messages(tr::now)
|
||||
: peer()->shortName();
|
||||
}
|
||||
|
||||
@@ -493,11 +507,14 @@ std::shared_ptr<Data::CloudImageView> PeerListRow::ensureUserpicView() {
|
||||
|
||||
PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback() {
|
||||
const auto saved = _isSavedMessagesChat;
|
||||
const auto replies = _isRepliesMessagesChat;
|
||||
const auto peer = this->peer();
|
||||
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 if (replies) {
|
||||
Ui::EmptyUserpic::PaintRepliesMessages(p, x, y, outerWidth, size);
|
||||
} else {
|
||||
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
|
||||
}
|
||||
@@ -603,6 +620,8 @@ void PeerListRow::paintDisabledCheckUserpic(
|
||||
|
||||
if (_isSavedMessagesChat) {
|
||||
Ui::EmptyUserpic::PaintSavedMessages(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
|
||||
} else if (_isRepliesMessagesChat) {
|
||||
Ui::EmptyUserpic::PaintRepliesMessages(p, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
|
||||
} else {
|
||||
peer()->paintUserpicLeft(p, _userpic, userpicLeft, userpicTop, outerWidth, userpicRadius * 2);
|
||||
}
|
||||
@@ -731,10 +750,12 @@ void PeerListContent::changeCheckState(
|
||||
}
|
||||
|
||||
void PeerListContent::addRowEntry(not_null<PeerListRow*> row) {
|
||||
if (_controller->respectSavedMessagesChat()
|
||||
&& !row->special()
|
||||
&& row->peer()->isSelf()) {
|
||||
row->setIsSavedMessagesChat(true);
|
||||
if (_controller->respectSavedMessagesChat() && !row->special()) {
|
||||
if (row->peer()->isSelf()) {
|
||||
row->setIsSavedMessagesChat(true);
|
||||
} else if (row->peer()->isRepliesChat()) {
|
||||
row->setIsRepliesMessagesChat(true);
|
||||
}
|
||||
}
|
||||
_rowsById.emplace(row->id(), row);
|
||||
if (!row->special()) {
|
||||
|
||||
@@ -146,12 +146,12 @@ public:
|
||||
void setIsSearchResult(bool isSearchResult) {
|
||||
_isSearchResult = isSearchResult;
|
||||
}
|
||||
bool isSavedMessagesChat() const {
|
||||
return _isSavedMessagesChat;
|
||||
}
|
||||
void setIsSavedMessagesChat(bool isSavedMessagesChat) {
|
||||
_isSavedMessagesChat = isSavedMessagesChat;
|
||||
}
|
||||
void setIsRepliesMessagesChat(bool isRepliesMessagesChat) {
|
||||
_isRepliesMessagesChat = isRepliesMessagesChat;
|
||||
}
|
||||
|
||||
template <typename UpdateCallback>
|
||||
void setChecked(
|
||||
@@ -234,6 +234,7 @@ private:
|
||||
bool _initialized : 1;
|
||||
bool _isSearchResult : 1;
|
||||
bool _isSavedMessagesChat : 1;
|
||||
bool _isRepliesMessagesChat : 1;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -584,8 +584,7 @@ void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
auto ChooseRecipientBoxController::createRow(
|
||||
not_null<History*> history) -> std::unique_ptr<Row> {
|
||||
const auto peer = history->peer;
|
||||
const auto skip = peer->isChannel()
|
||||
&& !peer->isMegagroup()
|
||||
&& !peer->canWrite();
|
||||
const auto skip = (peer->isBroadcast() && !peer->canWrite())
|
||||
|| peer->isRepliesChat();
|
||||
return skip ? nullptr : std::make_unique<Row>(history);
|
||||
}
|
||||
|
||||
@@ -203,8 +203,7 @@ public:
|
||||
|
||||
protected:
|
||||
void prepareViewHook() override;
|
||||
std::unique_ptr<Row> createRow(
|
||||
not_null<History*> history) override;
|
||||
std::unique_ptr<Row> createRow(not_null<History*> history) override;
|
||||
|
||||
private:
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
|
||||
@@ -576,11 +576,7 @@ void AddSpecialBoxController::showAdmin(
|
||||
}
|
||||
|
||||
// Finally show the admin.
|
||||
const auto currentRights = _additional.isCreator(user)
|
||||
? MTPChatAdminRights(MTP_chatAdminRights(
|
||||
MTP_flags(~MTPDchatAdminRights::Flag::f_add_admins
|
||||
| MTPDchatAdminRights::Flag::f_add_admins)))
|
||||
: adminRights
|
||||
const auto currentRights = adminRights
|
||||
? *adminRights
|
||||
: MTPChatAdminRights(MTP_chatAdminRights(MTP_flags(0)));
|
||||
auto box = Box<EditAdminBox>(
|
||||
@@ -618,6 +614,7 @@ void AddSpecialBoxController::editAdminDone(
|
||||
_additional.applyParticipant(MTP_channelParticipantCreator(
|
||||
MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank),
|
||||
MTP_int(user->bareId()),
|
||||
rights,
|
||||
MTP_string(rank)));
|
||||
} else if (rights.c_chatAdminRights().vflags().v == 0) {
|
||||
_additional.applyParticipant(MTP_channelParticipant(
|
||||
@@ -665,7 +662,7 @@ void AddSpecialBoxController::showRestricted(
|
||||
} else if (_additional.adminRights(user).has_value()
|
||||
|| _additional.isCreator(user)) {
|
||||
// The user is an admin or creator.
|
||||
if (_additional.canEditAdmin(user)) {
|
||||
if (!_additional.isCreator(user) && _additional.canEditAdmin(user)) {
|
||||
if (!sure) {
|
||||
_editBox = Ui::show(
|
||||
Box<ConfirmBox>(
|
||||
@@ -755,7 +752,7 @@ void AddSpecialBoxController::kickUser(
|
||||
if (_additional.adminRights(user).has_value()
|
||||
|| _additional.isCreator(user)) {
|
||||
// The user is an admin or creator.
|
||||
if (_additional.canEditAdmin(user)) {
|
||||
if (!_additional.isCreator(user) && _additional.canEditAdmin(user)) {
|
||||
if (!sure) {
|
||||
_editBox = Ui::show(
|
||||
Box<ConfirmBox>(
|
||||
|
||||
@@ -146,15 +146,12 @@ void Controller::choose(not_null<ChannelData*> chat) {
|
||||
Ui::Text::RichLangValue));
|
||||
}
|
||||
}
|
||||
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||
const auto sure = [=] {
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
const auto sure = [=](Fn<void()> &&close) {
|
||||
close();
|
||||
const auto onstack = _callback;
|
||||
onstack(chat);
|
||||
};
|
||||
*box = Ui::show(
|
||||
Ui::show(
|
||||
Box<ConfirmBox>(
|
||||
text,
|
||||
tr::lng_manage_discussion_group_link(tr::now),
|
||||
@@ -178,18 +175,15 @@ void Controller::choose(not_null<ChatData*> chat) {
|
||||
text.append(tr::lng_manage_discussion_group_warning(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue));
|
||||
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||
const auto sure = [=] {
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
const auto sure = [=](Fn<void()> &&close) {
|
||||
close();
|
||||
const auto done = [=](not_null<ChannelData*> chat) {
|
||||
const auto onstack = _callback;
|
||||
onstack(chat);
|
||||
};
|
||||
chat->session().api().migrateChat(chat, crl::guard(this, done));
|
||||
};
|
||||
*box = Ui::show(
|
||||
Ui::show(
|
||||
Box<ConfirmBox>(
|
||||
text,
|
||||
tr::lng_manage_discussion_group_link(tr::now),
|
||||
|
||||
@@ -47,70 +47,6 @@ constexpr auto kSecondsInDay = 24 * 60 * 60;
|
||||
constexpr auto kSecondsInWeek = 7 * kSecondsInDay;
|
||||
constexpr auto kAdminRoleLimit = 16;
|
||||
|
||||
enum class PasswordErrorType {
|
||||
None,
|
||||
NoPassword,
|
||||
Later,
|
||||
};
|
||||
|
||||
void SetCloudPassword(not_null<Ui::GenericBox*> box, not_null<UserData*> user) {
|
||||
user->session().api().passwordState(
|
||||
) | rpl::start_with_next([=] {
|
||||
using namespace Settings;
|
||||
const auto weak = Ui::MakeWeak(box);
|
||||
if (CheckEditCloudPassword(&user->session())) {
|
||||
box->getDelegate()->show(
|
||||
EditCloudPasswordBox(&user->session()));
|
||||
} else {
|
||||
box->getDelegate()->show(CloudPasswordAppOutdatedBox());
|
||||
}
|
||||
if (weak) {
|
||||
weak->closeBox();
|
||||
}
|
||||
}, box->lifetime());
|
||||
}
|
||||
|
||||
void TransferPasswordError(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<UserData*> user,
|
||||
PasswordErrorType error) {
|
||||
box->setTitle(tr::lng_rights_transfer_check());
|
||||
box->setWidth(st::transferCheckWidth);
|
||||
|
||||
auto text = tr::lng_rights_transfer_check_about(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold(user->shortName()),
|
||||
Ui::Text::WithEntities
|
||||
).append('\n').append('\n').append(
|
||||
tr::lng_rights_transfer_check_password(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue)
|
||||
).append('\n').append('\n').append(
|
||||
tr::lng_rights_transfer_check_session(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue)
|
||||
);
|
||||
if (error == PasswordErrorType::Later) {
|
||||
text.append('\n').append('\n').append(
|
||||
tr::lng_rights_transfer_check_later(
|
||||
tr::now,
|
||||
Ui::Text::RichLangValue));
|
||||
}
|
||||
box->addRow(object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
rpl::single(text),
|
||||
st::boxLabel));
|
||||
if (error == PasswordErrorType::Later) {
|
||||
box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); });
|
||||
} else {
|
||||
box->addButton(tr::lng_rights_transfer_set_password(), [=] {
|
||||
SetCloudPassword(box, user);
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class EditParticipantBox::Inner : public Ui::RpWidget {
|
||||
@@ -318,7 +254,7 @@ void EditAdminBox::prepare() {
|
||||
|
||||
const auto disabledMessages = [&] {
|
||||
auto result = std::map<Flags, QString>();
|
||||
if (!canSave() || (amCreator() && user()->isSelf())) {
|
||||
if (!canSave()) {
|
||||
result.emplace(
|
||||
~Flags(0),
|
||||
tr::lng_rights_about_admin_cant_edit(tr::now));
|
||||
@@ -327,7 +263,11 @@ void EditAdminBox::prepare() {
|
||||
disabledByDefaults,
|
||||
tr::lng_rights_permission_for_all(tr::now));
|
||||
if (const auto channel = peer()->asChannel()) {
|
||||
if (!channel->amCreator()) {
|
||||
if (amCreator() && user()->isSelf()) {
|
||||
result.emplace(
|
||||
~Flag::f_anonymous,
|
||||
tr::lng_rights_permission_cant_edit(tr::now));
|
||||
} else if (!channel->amCreator()) {
|
||||
result.emplace(
|
||||
~channel->adminRights(),
|
||||
tr::lng_rights_permission_cant_edit(tr::now));
|
||||
@@ -520,22 +460,17 @@ void EditAdminBox::transferOwnership() {
|
||||
}
|
||||
|
||||
bool EditAdminBox::handleTransferPasswordError(const RPCError &error) {
|
||||
const auto type = [&] {
|
||||
const auto &type = error.type();
|
||||
if (type == qstr("PASSWORD_MISSING")) {
|
||||
return PasswordErrorType::NoPassword;
|
||||
} else if (type.startsWith(qstr("PASSWORD_TOO_FRESH_"))
|
||||
|| type.startsWith(qstr("SESSION_TOO_FRESH_"))) {
|
||||
return PasswordErrorType::Later;
|
||||
}
|
||||
return PasswordErrorType::None;
|
||||
}();
|
||||
if (type == PasswordErrorType::None) {
|
||||
return false;
|
||||
const auto session = &user()->session();
|
||||
auto about = tr::lng_rights_transfer_check_about(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold(user()->shortName()),
|
||||
Ui::Text::WithEntities);
|
||||
if (auto box = PrePasswordErrorBox(error, session, std::move(about))) {
|
||||
getDelegate()->show(std::move(box));
|
||||
return true;
|
||||
}
|
||||
|
||||
getDelegate()->show(Box(TransferPasswordError, user(), type));
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditAdminBox::transferOwnershipChecked() {
|
||||
@@ -639,7 +574,9 @@ void EditAdminBox::sendTransferRequestFrom(
|
||||
|
||||
void EditAdminBox::refreshAboutAddAdminsText(bool canAddAdmins) {
|
||||
_aboutAddAdmins->setText([&] {
|
||||
if (!canSave() || (amCreator() && user()->isSelf())) {
|
||||
if (amCreator() && user()->isSelf()) {
|
||||
return QString();
|
||||
} else if (!canSave()) {
|
||||
return tr::lng_rights_about_admin_cant_edit(tr::now);
|
||||
} else if (canAddAdmins) {
|
||||
return tr::lng_rights_about_add_admins_yes(tr::now);
|
||||
|
||||
@@ -564,6 +564,12 @@ UserData *ParticipantsAdditionalData::applyCreator(
|
||||
const MTPDchannelParticipantCreator &data) {
|
||||
if (const auto user = applyRegular(data.vuser_id())) {
|
||||
_creator = user;
|
||||
_adminRights[user] = data.vadmin_rights();
|
||||
if (user->isSelf()) {
|
||||
_adminCanEdit.emplace(user);
|
||||
} else {
|
||||
_adminCanEdit.erase(user);
|
||||
}
|
||||
if (const auto rank = data.vrank()) {
|
||||
_adminRanks[user] = qs(*rank);
|
||||
} else {
|
||||
@@ -1459,11 +1465,7 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
|
||||
|
||||
void ParticipantsBoxController::showAdmin(not_null<UserData*> user) {
|
||||
const auto adminRights = _additional.adminRights(user);
|
||||
const auto currentRights = _additional.isCreator(user)
|
||||
? MTPChatAdminRights(MTP_chatAdminRights(
|
||||
MTP_flags(~MTPDchatAdminRights::Flag::f_add_admins
|
||||
| MTPDchatAdminRights::Flag::f_add_admins)))
|
||||
: adminRights
|
||||
const auto currentRights = adminRights
|
||||
? *adminRights
|
||||
: MTPChatAdminRights(MTP_chatAdminRights(MTP_flags(0)));
|
||||
auto box = Box<EditAdminBox>(
|
||||
@@ -1504,6 +1506,7 @@ void ParticipantsBoxController::editAdminDone(
|
||||
_additional.applyParticipant(MTP_channelParticipantCreator(
|
||||
MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank),
|
||||
MTP_int(user->bareId()),
|
||||
rights,
|
||||
MTP_string(rank)));
|
||||
} else if (rights.c_chatAdminRights().vflags().v == 0) {
|
||||
_additional.applyParticipant(MTP_channelParticipant(
|
||||
@@ -1783,6 +1786,7 @@ std::unique_ptr<PeerListRow> ParticipantsBoxController::createRow(
|
||||
auto row = std::make_unique<PeerListRowWithLink>(user);
|
||||
refreshCustomStatus(row.get());
|
||||
if (_role == Role::Admins
|
||||
&& !_additional.isCreator(user)
|
||||
&& _additional.adminRights(user).has_value()
|
||||
&& _additional.canEditAdmin(user)) {
|
||||
row->setActionLink(tr::lng_profile_kick(tr::now));
|
||||
|
||||
@@ -135,6 +135,7 @@ std::vector<std::pair<ChatAdminRights, QString>> AdminRightLabels(
|
||||
? tr::lng_rights_group_invite_link(tr::now)
|
||||
: tr::lng_rights_group_invite(tr::now) },
|
||||
{ Flag::f_pin_messages, tr::lng_rights_group_pin(tr::now) },
|
||||
{ Flag::f_anonymous, tr::lng_rights_group_anonymous(tr::now) },
|
||||
{ Flag::f_add_admins, tr::lng_rights_add_admins(tr::now) },
|
||||
};
|
||||
} else {
|
||||
|
||||
@@ -546,17 +546,14 @@ void Controller::revokeInviteLink() {
|
||||
}
|
||||
|
||||
void Controller::exportInviteLink(const QString &confirmation) {
|
||||
const auto boxPointer = std::make_shared<QPointer<ConfirmBox>>();
|
||||
const auto callback = crl::guard(this, [=] {
|
||||
if (const auto strong = *boxPointer) {
|
||||
strong->closeBox();
|
||||
}
|
||||
const auto callback = crl::guard(this, [=](Fn<void()> &&close) {
|
||||
close();
|
||||
_peer->session().api().exportInviteLink(_peer->migrateToOrMe());
|
||||
});
|
||||
auto box = Box<ConfirmBox>(
|
||||
confirmation,
|
||||
std::move(callback));
|
||||
*boxPointer = Ui::show(std::move(box), Ui::LayerOption::KeepOther);
|
||||
Ui::show(std::move(box), Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
bool Controller::canEditInviteLink() const {
|
||||
|
||||
@@ -9,8 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "history/history_item.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
@@ -18,6 +20,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mainwindow.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "core/application.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/window_peer_menu.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_profile.h"
|
||||
@@ -207,3 +211,18 @@ void ReportBox::updateMaxHeight() {
|
||||
}
|
||||
setDimensions(st::boxWidth, newHeight);
|
||||
}
|
||||
|
||||
void BlockSenderFromRepliesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId id) {
|
||||
const auto item = controller->session().data().message(id);
|
||||
Assert(item != nullptr);
|
||||
|
||||
PeerMenuBlockUserBox(
|
||||
box,
|
||||
&controller->window(),
|
||||
item->senderOriginal(),
|
||||
true,
|
||||
Window::ClearReply{ id });
|
||||
}
|
||||
|
||||
@@ -8,8 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace Ui {
|
||||
template <typename Enum>
|
||||
class RadioenumGroup;
|
||||
@@ -60,3 +65,8 @@ private:
|
||||
mtpRequestId _requestId = 0;
|
||||
|
||||
};
|
||||
|
||||
void BlockSenderFromRepliesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId id);
|
||||
|
||||
@@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/grouped_layout.h"
|
||||
#include "ui/text_options.h"
|
||||
#include "ui/special_buttons.h"
|
||||
@@ -41,7 +42,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_session_controller.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "layout.h"
|
||||
#include "facades.h" // App::LambdaDelayed.
|
||||
#include "app.h"
|
||||
#include "styles/style_history.h"
|
||||
@@ -352,7 +352,7 @@ AlbumThumb::AlbumThumb(
|
||||
} else {
|
||||
auto fileinfo = QFileInfo(filepath);
|
||||
_name = fileinfo.fileName();
|
||||
_status = formatSizeText(fileinfo.size());
|
||||
_status = Ui::FormatSizeText(fileinfo.size());
|
||||
}
|
||||
_nameWidth = st::semiboldFont->width(_name);
|
||||
if (_nameWidth > availableFileWidth) {
|
||||
@@ -766,11 +766,11 @@ SingleMediaPreview *SingleMediaPreview::Create(
|
||||
auto preview = QImage();
|
||||
bool animated = false;
|
||||
bool animationPreview = false;
|
||||
if (const auto image = base::get_if<FileMediaInformation::Image>(
|
||||
if (const auto image = std::get_if<FileMediaInformation::Image>(
|
||||
&file.information->media)) {
|
||||
preview = image->data;
|
||||
animated = animationPreview = image->animated;
|
||||
} else if (const auto video = base::get_if<FileMediaInformation::Video>(
|
||||
} else if (const auto video = std::get_if<FileMediaInformation::Video>(
|
||||
&file.information->media)) {
|
||||
preview = video->thumbnail;
|
||||
animated = true;
|
||||
@@ -1004,10 +1004,10 @@ void SingleFilePreview::prepareThumb(const QImage &preview) {
|
||||
|
||||
void SingleFilePreview::preparePreview(const Storage::PreparedFile &file) {
|
||||
auto preview = QImage();
|
||||
if (const auto image = base::get_if<FileMediaInformation::Image>(
|
||||
if (const auto image = std::get_if<FileMediaInformation::Image>(
|
||||
&file.information->media)) {
|
||||
preview = image->data;
|
||||
} else if (const auto video = base::get_if<FileMediaInformation::Video>(
|
||||
} else if (const auto video = std::get_if<FileMediaInformation::Video>(
|
||||
&file.information->media)) {
|
||||
preview = video->thumbnail;
|
||||
}
|
||||
@@ -1034,7 +1034,7 @@ void SingleFilePreview::preparePreview(const Storage::PreparedFile &file) {
|
||||
auto songTitle = QString();
|
||||
auto songPerformer = QString();
|
||||
if (file.information) {
|
||||
if (const auto song = base::get_if<FileMediaInformation::Song>(
|
||||
if (const auto song = std::get_if<FileMediaInformation::Song>(
|
||||
&file.information->media)) {
|
||||
songTitle = song->title;
|
||||
songPerformer = song->performer;
|
||||
@@ -1050,7 +1050,7 @@ void SingleFilePreview::preparePreview(const Storage::PreparedFile &file) {
|
||||
st::semiboldTextStyle,
|
||||
nameString,
|
||||
Ui::NameTextOptions());
|
||||
_statusText = formatSizeText(fileinfo.size());
|
||||
_statusText = Ui::FormatSizeText(fileinfo.size());
|
||||
_statusWidth = qMax(
|
||||
_nameText.maxWidth(),
|
||||
st::normalFont->width(_statusText));
|
||||
|
||||
@@ -7,24 +7,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/sessions_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_authorizations.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "window/window_session_controller.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -32,7 +31,63 @@ constexpr auto kSessionsShortPollTimeout = 60 * crl::time(1000);
|
||||
|
||||
} // namespace
|
||||
|
||||
class SessionsBox::List : public Ui::RpWidget {
|
||||
class SessionsContent : public Ui::RpWidget {
|
||||
public:
|
||||
SessionsContent(QWidget*, not_null<Main::Session*> session);
|
||||
|
||||
void setupContent();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
Entry() = default;
|
||||
Entry(const Api::Authorizations::Entry &entry)
|
||||
: hash(entry.hash)
|
||||
, incomplete(entry.incomplete)
|
||||
, activeTime(entry.activeTime)
|
||||
, name(st::sessionNameStyle, entry.name)
|
||||
, active(st::sessionWhenStyle, entry.active)
|
||||
, info(st::sessionInfoStyle, entry.info)
|
||||
, ip(st::sessionInfoStyle, entry.ip) {
|
||||
};
|
||||
|
||||
uint64 hash = 0;
|
||||
|
||||
bool incomplete = false;
|
||||
TimeId activeTime = 0;
|
||||
Ui::Text::String name, active, info, ip;
|
||||
};
|
||||
struct Full {
|
||||
Entry current;
|
||||
std::vector<Entry> incomplete;
|
||||
std::vector<Entry> list;
|
||||
};
|
||||
class Inner;
|
||||
class List;
|
||||
|
||||
void shortPollSessions();
|
||||
void parse(const Api::Authorizations::List &list);
|
||||
|
||||
void terminate(Fn<void()> terminateRequest, QString message);
|
||||
void terminateOne(uint64 hash);
|
||||
void terminateAll();
|
||||
|
||||
const not_null<Api::Authorizations*> _authorizations;
|
||||
|
||||
rpl::variable<bool> _loading = false;
|
||||
Full _data;
|
||||
|
||||
object_ptr<Inner> _inner;
|
||||
QPointer<ConfirmBox> _terminateBox;
|
||||
|
||||
base::Timer _shortPollTimer;
|
||||
|
||||
};
|
||||
|
||||
class SessionsContent::List : public Ui::RpWidget {
|
||||
public:
|
||||
List(QWidget *parent);
|
||||
|
||||
@@ -43,11 +98,20 @@ public:
|
||||
void terminating(uint64 hash, bool terminating);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
private:
|
||||
struct RowWidth {
|
||||
int available = 0;
|
||||
int info = 0;
|
||||
};
|
||||
RowWidth _rowWidth;
|
||||
|
||||
void computeRowWidth();
|
||||
|
||||
std::vector<Entry> _items;
|
||||
std::map<uint64, std::unique_ptr<Ui::IconButton>> _terminateButtons;
|
||||
rpl::event_stream<uint64> _terminate;
|
||||
@@ -55,7 +119,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class SessionsBox::Inner : public Ui::RpWidget {
|
||||
class SessionsContent::Inner : public Ui::RpWidget {
|
||||
public:
|
||||
Inner(QWidget *parent);
|
||||
|
||||
@@ -75,22 +139,21 @@ private:
|
||||
|
||||
};
|
||||
|
||||
SessionsBox::SessionsBox(QWidget*, not_null<Main::Session*> session)
|
||||
: _session(session)
|
||||
, _api(&_session->mtp())
|
||||
SessionsContent::SessionsContent(QWidget*, not_null<Main::Session*> session)
|
||||
: _authorizations(&session->api().authorizations())
|
||||
, _inner(this)
|
||||
, _shortPollTimer([=] { shortPollSessions(); }) {
|
||||
}
|
||||
|
||||
void SessionsBox::prepare() {
|
||||
setTitle(tr::lng_sessions_other_header());
|
||||
|
||||
addButton(tr::lng_close(), [=] { closeBox(); });
|
||||
|
||||
setDimensions(st::boxWideWidth, st::sessionsHeight);
|
||||
|
||||
_inner = setInnerWidget(object_ptr<Inner>(this), st::sessionsScroll);
|
||||
void SessionsContent::setupContent() {
|
||||
_inner->resize(width(), st::noContactsHeight);
|
||||
|
||||
_inner->heightValue(
|
||||
) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](int height) {
|
||||
resize(width(), height);
|
||||
}, _inner->lifetime());
|
||||
|
||||
_inner->terminateOne(
|
||||
) | rpl::start_with_next([=](uint64 hash) {
|
||||
terminateOne(hash);
|
||||
@@ -101,34 +164,58 @@ void SessionsBox::prepare() {
|
||||
terminateAll();
|
||||
}, lifetime());
|
||||
|
||||
_session->data().newAuthorizationChecks(
|
||||
) | rpl::start_with_next([=] {
|
||||
shortPollSessions();
|
||||
_loading.changes(
|
||||
) | rpl::start_with_next([=](bool value) {
|
||||
_inner->setVisible(!value);
|
||||
}, lifetime());
|
||||
|
||||
setLoading(true);
|
||||
_authorizations->listChanges(
|
||||
) | rpl::start_with_next([=](const Api::Authorizations::List &list) {
|
||||
parse(list);
|
||||
}, lifetime());
|
||||
|
||||
_loading = true;
|
||||
shortPollSessions();
|
||||
}
|
||||
|
||||
void SessionsBox::setLoading(bool loading) {
|
||||
if (_loading != loading) {
|
||||
_loading = loading;
|
||||
setInnerVisible(!_loading);
|
||||
void SessionsContent::parse(const Api::Authorizations::List &list) {
|
||||
if (list.empty()) {
|
||||
return;
|
||||
}
|
||||
_data = Full();
|
||||
for (const auto &auth : list) {
|
||||
auto entry = Entry(auth);
|
||||
if (!entry.hash) {
|
||||
_data.current = std::move(entry);
|
||||
} else if (entry.incomplete) {
|
||||
_data.incomplete.push_back(std::move(entry));
|
||||
} else {
|
||||
_data.list.push_back(std::move(entry));
|
||||
}
|
||||
}
|
||||
|
||||
_loading = false;
|
||||
|
||||
ranges::sort(_data.list, std::greater<>(), &Entry::activeTime);
|
||||
ranges::sort(_data.incomplete, std::greater<>(), &Entry::activeTime);
|
||||
|
||||
_inner->showData(_data);
|
||||
|
||||
_shortPollTimer.callOnce(kSessionsShortPollTimeout);
|
||||
}
|
||||
|
||||
void SessionsBox::resizeEvent(QResizeEvent *e) {
|
||||
BoxContent::resizeEvent(e);
|
||||
void SessionsContent::resizeEvent(QResizeEvent *e) {
|
||||
RpWidget::resizeEvent(e);
|
||||
|
||||
_inner->resize(width(), _inner->height());
|
||||
}
|
||||
|
||||
void SessionsBox::paintEvent(QPaintEvent *e) {
|
||||
BoxContent::paintEvent(e);
|
||||
void SessionsContent::paintEvent(QPaintEvent *e) {
|
||||
RpWidget::paintEvent(e);
|
||||
|
||||
Painter p(this);
|
||||
|
||||
if (_loading) {
|
||||
if (_loading.current()) {
|
||||
p.setFont(st::noContactsFont);
|
||||
p.setPen(st::noContactsColor);
|
||||
p.drawText(
|
||||
@@ -138,166 +225,43 @@ void SessionsBox::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
void SessionsBox::got(const MTPaccount_Authorizations &result) {
|
||||
_shortPollRequest = 0;
|
||||
setLoading(false);
|
||||
_data = Full();
|
||||
|
||||
result.match([&](const MTPDaccount_authorizations &data) {
|
||||
const auto &list = data.vauthorizations().v;
|
||||
for (const auto &auth : list) {
|
||||
auth.match([&](const MTPDauthorization &data) {
|
||||
auto entry = ParseEntry(data);
|
||||
if (!entry.hash) {
|
||||
_data.current = std::move(entry);
|
||||
} else if (entry.incomplete) {
|
||||
_data.incomplete.push_back(std::move(entry));
|
||||
} else {
|
||||
_data.list.push_back(std::move(entry));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const auto getActiveTime = [](const Entry &entry) {
|
||||
return entry.activeTime;
|
||||
};
|
||||
ranges::sort(_data.list, std::greater<>(), getActiveTime);
|
||||
ranges::sort(_data.incomplete, std::greater<>(), getActiveTime);
|
||||
|
||||
_inner->showData(_data);
|
||||
|
||||
_shortPollTimer.callOnce(kSessionsShortPollTimeout);
|
||||
}
|
||||
|
||||
SessionsBox::Entry SessionsBox::ParseEntry(const MTPDauthorization &data) {
|
||||
auto result = Entry();
|
||||
|
||||
result.hash = data.is_current() ? 0 : data.vhash().v;
|
||||
result.incomplete = data.is_password_pending();
|
||||
|
||||
auto appName = QString();
|
||||
auto appVer = qs(data.vapp_version());
|
||||
const auto systemVer = qs(data.vsystem_version());
|
||||
const auto deviceModel = qs(data.vdevice_model());
|
||||
const auto apiId = data.vapi_id().v;
|
||||
if (apiId == 2040 || apiId == 17349) {
|
||||
appName = (apiId == 2040)
|
||||
? qstr("Telegram Desktop")
|
||||
: qstr("Telegram Desktop (GitHub)");
|
||||
//if (systemVer == qstr("windows")) {
|
||||
// deviceModel = qsl("Windows");
|
||||
//} else if (systemVer == qstr("os x")) {
|
||||
// deviceModel = qsl("OS X");
|
||||
//} else if (systemVer == qstr("linux")) {
|
||||
// deviceModel = qsl("Linux");
|
||||
//}
|
||||
if (appVer == QString::number(appVer.toInt())) {
|
||||
const auto ver = appVer.toInt();
|
||||
appVer = QString("%1.%2"
|
||||
).arg(ver / 1000000
|
||||
).arg((ver % 1000000) / 1000)
|
||||
+ ((ver % 1000)
|
||||
? ('.' + QString::number(ver % 1000))
|
||||
: QString());
|
||||
//} else {
|
||||
// appVer = QString();
|
||||
}
|
||||
void SessionsContent::shortPollSessions() {
|
||||
const auto left = kSessionsShortPollTimeout
|
||||
- (crl::now() - _authorizations->lastReceivedTime());
|
||||
if (left > 0) {
|
||||
parse(_authorizations->list());
|
||||
_shortPollTimer.cancel();
|
||||
_shortPollTimer.callOnce(left);
|
||||
} else {
|
||||
appName = qs(data.vapp_name());// +qsl(" for ") + qs(d.vplatform());
|
||||
if (appVer.indexOf('(') >= 0) {
|
||||
appVer = appVer.mid(appVer.indexOf('('));
|
||||
}
|
||||
_authorizations->reload();
|
||||
}
|
||||
result.name = appName;
|
||||
if (!appVer.isEmpty()) {
|
||||
result.name += ' ' + appVer;
|
||||
}
|
||||
|
||||
const auto country = qs(data.vcountry());
|
||||
const auto platform = qs(data.vplatform());
|
||||
//const auto &countries = countriesByISO2();
|
||||
//const auto j = countries.constFind(country);
|
||||
//if (j != countries.cend()) {
|
||||
// country = QString::fromUtf8(j.value()->name);
|
||||
//}
|
||||
|
||||
result.activeTime = data.vdate_active().v
|
||||
? data.vdate_active().v
|
||||
: data.vdate_created().v;
|
||||
result.info = qs(data.vdevice_model()) + qstr(", ") + (platform.isEmpty() ? QString() : platform + ' ') + qs(data.vsystem_version());
|
||||
result.ip = qs(data.vip()) + (country.isEmpty() ? QString() : QString::fromUtf8(" \xe2\x80\x93 ") + country);
|
||||
if (!result.hash) {
|
||||
result.active = tr::lng_status_online(tr::now);
|
||||
result.activeWidth = st::sessionWhenFont->width(tr::lng_status_online(tr::now));
|
||||
} else {
|
||||
const auto now = QDateTime::currentDateTime();
|
||||
const auto lastTime = base::unixtime::parse(result.activeTime);
|
||||
const auto nowDate = now.date();
|
||||
const auto lastDate = lastTime.date();
|
||||
if (lastDate == nowDate) {
|
||||
result.active = lastTime.toString(cTimeFormat());
|
||||
} else if (lastDate.year() == nowDate.year()
|
||||
&& lastDate.weekNumber() == nowDate.weekNumber()) {
|
||||
result.active = langDayOfWeek(lastDate);
|
||||
} else {
|
||||
result.active = lastDate.toString(qsl("d.MM.yy"));
|
||||
}
|
||||
result.activeWidth = st::sessionWhenFont->width(result.active);
|
||||
}
|
||||
|
||||
ResizeEntry(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SessionsBox::ResizeEntry(Entry &entry) {
|
||||
const auto available = st::boxWideWidth
|
||||
- st::sessionPadding.left()
|
||||
- st::sessionTerminateSkip;
|
||||
const auto availableInList = available
|
||||
- st::sessionTerminate.iconPosition.x();
|
||||
const auto availableListInfo = available - st::sessionTerminate.width;
|
||||
|
||||
const auto resize = [](
|
||||
const style::font &font,
|
||||
QString &string,
|
||||
int &stringWidth,
|
||||
int available) {
|
||||
stringWidth = font->width(string);
|
||||
if (stringWidth > available) {
|
||||
string = font->elided(string, available);
|
||||
stringWidth = font->width(string);
|
||||
}
|
||||
};
|
||||
const auto forName = entry.hash ? availableInList : available;
|
||||
const auto forInfo = entry.hash ? availableListInfo : available;
|
||||
resize(st::sessionNameFont, entry.name, entry.nameWidth, forName);
|
||||
resize(st::sessionInfoFont, entry.info, entry.infoWidth, forInfo);
|
||||
resize(st::sessionInfoFont, entry.ip, entry.ipWidth, available);
|
||||
}
|
||||
|
||||
void SessionsBox::shortPollSessions() {
|
||||
if (_shortPollRequest) {
|
||||
return;
|
||||
}
|
||||
_shortPollRequest = _api.request(MTPaccount_GetAuthorizations(
|
||||
)).done([=](const MTPaccount_Authorizations &result) {
|
||||
got(result);
|
||||
}).send();
|
||||
update();
|
||||
}
|
||||
|
||||
void SessionsBox::terminateOne(uint64 hash) {
|
||||
if (_terminateBox) _terminateBox->deleteLater();
|
||||
void SessionsContent::terminate(Fn<void()> terminateRequest, QString message) {
|
||||
if (_terminateBox) {
|
||||
_terminateBox->deleteLater();
|
||||
}
|
||||
const auto callback = crl::guard(this, [=] {
|
||||
if (_terminateBox) {
|
||||
_terminateBox->closeBox();
|
||||
_terminateBox = nullptr;
|
||||
}
|
||||
_api.request(MTPaccount_ResetAuthorization(
|
||||
MTP_long(hash)
|
||||
)).done([=](const MTPBool &result) {
|
||||
terminateRequest();
|
||||
});
|
||||
_terminateBox = Ui::show(
|
||||
Box<ConfirmBox>(
|
||||
message,
|
||||
tr::lng_settings_reset_button(tr::now),
|
||||
st::attentionBoxButton,
|
||||
callback),
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
void SessionsContent::terminateOne(uint64 hash) {
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
auto callback = [=] {
|
||||
auto done = crl::guard(weak, [=](const MTPBool &result) {
|
||||
_inner->terminatingOne(hash, false);
|
||||
const auto getHash = [](const Entry &entry) {
|
||||
return entry.hash;
|
||||
@@ -310,52 +274,40 @@ void SessionsBox::terminateOne(uint64 hash) {
|
||||
removeByHash(_data.incomplete);
|
||||
removeByHash(_data.list);
|
||||
_inner->showData(_data);
|
||||
}).fail([=](const RPCError &error) {
|
||||
});
|
||||
auto fail = crl::guard(weak, [=](const RPCError &error) {
|
||||
_inner->terminatingOne(hash, false);
|
||||
}).send();
|
||||
});
|
||||
_authorizations->requestTerminate(
|
||||
std::move(done),
|
||||
std::move(fail),
|
||||
hash);
|
||||
_inner->terminatingOne(hash, true);
|
||||
});
|
||||
_terminateBox = Ui::show(
|
||||
Box<ConfirmBox>(
|
||||
tr::lng_settings_reset_one_sure(tr::now),
|
||||
tr::lng_settings_reset_button(tr::now),
|
||||
st::attentionBoxButton,
|
||||
callback),
|
||||
Ui::LayerOption::KeepOther);
|
||||
};
|
||||
terminate(std::move(callback), tr::lng_settings_reset_one_sure(tr::now));
|
||||
}
|
||||
|
||||
void SessionsBox::terminateAll() {
|
||||
if (_terminateBox) _terminateBox->deleteLater();
|
||||
const auto callback = crl::guard(this, [=] {
|
||||
if (_terminateBox) {
|
||||
_terminateBox->closeBox();
|
||||
_terminateBox = nullptr;
|
||||
}
|
||||
_api.request(MTPauth_ResetAuthorizations(
|
||||
)).done([=](const MTPBool &result) {
|
||||
_api.request(base::take(_shortPollRequest)).cancel();
|
||||
void SessionsContent::terminateAll() {
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
auto callback = [=] {
|
||||
const auto reset = crl::guard(weak, [=] {
|
||||
_authorizations->cancelCurrentRequest();
|
||||
shortPollSessions();
|
||||
}).fail([=](const RPCError &result) {
|
||||
_api.request(base::take(_shortPollRequest)).cancel();
|
||||
shortPollSessions();
|
||||
}).send();
|
||||
setLoading(true);
|
||||
});
|
||||
_terminateBox = Ui::show(
|
||||
Box<ConfirmBox>(
|
||||
tr::lng_settings_reset_sure(tr::now),
|
||||
tr::lng_settings_reset_button(tr::now),
|
||||
st::attentionBoxButton,
|
||||
callback),
|
||||
Ui::LayerOption::KeepOther);
|
||||
});
|
||||
_authorizations->requestTerminate(
|
||||
[=](const MTPBool &result) { reset(); },
|
||||
[=](const RPCError &result) { reset(); });
|
||||
_loading = true;
|
||||
};
|
||||
terminate(std::move(callback), tr::lng_settings_reset_sure(tr::now));
|
||||
}
|
||||
|
||||
SessionsBox::Inner::Inner(QWidget *parent)
|
||||
SessionsContent::Inner::Inner(QWidget *parent)
|
||||
: RpWidget(parent) {
|
||||
setupContent();
|
||||
}
|
||||
|
||||
void SessionsBox::Inner::setupContent() {
|
||||
void SessionsContent::Inner::setupContent() {
|
||||
using namespace Settings;
|
||||
using namespace rpl::mappers;
|
||||
|
||||
@@ -418,32 +370,40 @@ void SessionsBox::Inner::setupContent() {
|
||||
Ui::ResizeFitChild(this, content);
|
||||
}
|
||||
|
||||
void SessionsBox::Inner::showData(const Full &data) {
|
||||
void SessionsContent::Inner::showData(const Full &data) {
|
||||
_current->showData({ &data.current, &data.current + 1 });
|
||||
_list->showData(data.list);
|
||||
_incomplete->showData(data.incomplete);
|
||||
}
|
||||
|
||||
rpl::producer<> SessionsBox::Inner::terminateAll() const {
|
||||
rpl::producer<> SessionsContent::Inner::terminateAll() const {
|
||||
return _terminateAll->clicks() | rpl::to_empty;
|
||||
}
|
||||
|
||||
rpl::producer<uint64> SessionsBox::Inner::terminateOne() const {
|
||||
rpl::producer<uint64> SessionsContent::Inner::terminateOne() const {
|
||||
return rpl::merge(
|
||||
_incomplete->terminate(),
|
||||
_list->terminate());
|
||||
}
|
||||
|
||||
void SessionsBox::Inner::terminatingOne(uint64 hash, bool terminating) {
|
||||
void SessionsContent::Inner::terminatingOne(uint64 hash, bool terminating) {
|
||||
_incomplete->terminating(hash, terminating);
|
||||
_list->terminating(hash, terminating);
|
||||
}
|
||||
|
||||
SessionsBox::List::List(QWidget *parent) : RpWidget(parent) {
|
||||
SessionsContent::List::List(QWidget *parent) : RpWidget(parent) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
}
|
||||
|
||||
void SessionsBox::List::showData(gsl::span<const Entry> items) {
|
||||
void SessionsContent::List::resizeEvent(QResizeEvent *e) {
|
||||
RpWidget::resizeEvent(e);
|
||||
|
||||
computeRowWidth();
|
||||
}
|
||||
|
||||
void SessionsContent::List::showData(gsl::span<const Entry> items) {
|
||||
computeRowWidth();
|
||||
|
||||
auto buttons = base::take(_terminateButtons);
|
||||
_items.clear();
|
||||
_items.insert(begin(_items), items.begin(), items.end());
|
||||
@@ -466,24 +426,27 @@ void SessionsBox::List::showData(gsl::span<const Entry> items) {
|
||||
_terminate.fire_copy(hash);
|
||||
});
|
||||
button->show();
|
||||
button->moveToRight(
|
||||
st::sessionTerminateSkip,
|
||||
((_terminateButtons.size() - 1) * st::sessionHeight
|
||||
+ st::sessionTerminateTop));
|
||||
const auto number = _terminateButtons.size() - 1;
|
||||
widthValue(
|
||||
) | rpl::start_with_next([=] {
|
||||
button->moveToRight(
|
||||
st::sessionTerminateSkip,
|
||||
(number * st::sessionHeight + st::sessionTerminateTop));
|
||||
}, lifetime());
|
||||
}
|
||||
resizeToWidth(width());
|
||||
_itemsCount.fire(_items.size());
|
||||
}
|
||||
|
||||
rpl::producer<int> SessionsBox::List::itemsCount() const {
|
||||
rpl::producer<int> SessionsContent::List::itemsCount() const {
|
||||
return _itemsCount.events_starting_with(_items.size());
|
||||
}
|
||||
|
||||
rpl::producer<uint64> SessionsBox::List::terminate() const {
|
||||
rpl::producer<uint64> SessionsContent::List::terminate() const {
|
||||
return _terminate.events();
|
||||
}
|
||||
|
||||
void SessionsBox::List::terminating(uint64 hash, bool terminating) {
|
||||
void SessionsContent::List::terminating(uint64 hash, bool terminating) {
|
||||
const auto i = _terminateButtons.find(hash);
|
||||
if (i != _terminateButtons.cend()) {
|
||||
if (terminating) {
|
||||
@@ -495,11 +458,21 @@ void SessionsBox::List::terminating(uint64 hash, bool terminating) {
|
||||
}
|
||||
}
|
||||
|
||||
int SessionsBox::List::resizeGetHeight(int newWidth) {
|
||||
int SessionsContent::List::resizeGetHeight(int newWidth) {
|
||||
return _items.size() * st::sessionHeight;
|
||||
}
|
||||
|
||||
void SessionsBox::List::paintEvent(QPaintEvent *e) {
|
||||
void SessionsContent::List::computeRowWidth() {
|
||||
const auto available = width()
|
||||
- st::sessionPadding.left()
|
||||
- st::sessionTerminateSkip;
|
||||
_rowWidth = {
|
||||
.available = available,
|
||||
.info = available - st::sessionTerminate.width,
|
||||
};
|
||||
}
|
||||
|
||||
void SessionsContent::List::paintEvent(QPaintEvent *e) {
|
||||
QRect r(e->rect());
|
||||
Painter p(this);
|
||||
|
||||
@@ -513,6 +486,7 @@ void SessionsBox::List::paintEvent(QPaintEvent *e) {
|
||||
0,
|
||||
count);
|
||||
|
||||
const auto available = _rowWidth.available;
|
||||
const auto x = st::sessionPadding.left();
|
||||
const auto y = st::sessionPadding.top();
|
||||
const auto w = width();
|
||||
@@ -522,23 +496,66 @@ void SessionsBox::List::paintEvent(QPaintEvent *e) {
|
||||
for (auto i = from; i != till; ++i) {
|
||||
const auto &entry = _items[i];
|
||||
|
||||
p.setFont(st::sessionNameFont);
|
||||
const auto activeW = entry.active.maxWidth();
|
||||
const auto nameW = available
|
||||
- activeW
|
||||
- st::sessionNamePadding.right();
|
||||
const auto nameH = entry.name.style()->font->height;
|
||||
const auto infoW = entry.hash ? _rowWidth.info : available;
|
||||
const auto infoH = entry.info.style()->font->height;
|
||||
|
||||
p.setPen(entry.hash ? st::sessionWhenFg : st::contactsStatusFgOnline);
|
||||
entry.active.drawRight(p, xact, y, activeW, w);
|
||||
|
||||
p.setPen(st::sessionNameFg);
|
||||
p.drawTextLeft(x, y, w, entry.name, entry.nameWidth);
|
||||
entry.name.drawLeftElided(p, x, y, nameW, w);
|
||||
|
||||
p.setFont(st::sessionWhenFont);
|
||||
p.setPen(st::sessionWhenFg);
|
||||
p.drawTextRight(xact, y, w, entry.active, entry.activeWidth);
|
||||
|
||||
const auto name = st::sessionNameFont->height;
|
||||
p.setFont(st::sessionInfoFont);
|
||||
p.setPen(st::boxTextFg);
|
||||
p.drawTextLeft(x, y + name, w, entry.info, entry.infoWidth);
|
||||
entry.info.drawLeftElided(p, x, y + nameH, infoW, w);
|
||||
|
||||
const auto info = st::sessionInfoFont->height;
|
||||
p.setPen(st::sessionInfoFg);
|
||||
p.drawTextLeft(x, y + name + info, w, entry.ip, entry.ipWidth);
|
||||
entry.ip.drawLeftElided(p, x, y + nameH + infoH, available, w);
|
||||
|
||||
p.translate(0, st::sessionHeight);
|
||||
}
|
||||
}
|
||||
|
||||
SessionsBox::SessionsBox(QWidget*, not_null<Main::Session*> session)
|
||||
: _session(session) {
|
||||
}
|
||||
|
||||
void SessionsBox::prepare() {
|
||||
setTitle(tr::lng_sessions_other_header());
|
||||
|
||||
addButton(tr::lng_close(), [=] { closeBox(); });
|
||||
|
||||
const auto w = st::boxWideWidth;
|
||||
|
||||
const auto content = setInnerWidget(
|
||||
object_ptr<SessionsContent>(this, _session),
|
||||
st::sessionsScroll);
|
||||
content->resize(w, st::noContactsHeight);
|
||||
content->setupContent();
|
||||
|
||||
setDimensions(w, st::sessionsHeight);
|
||||
}
|
||||
|
||||
namespace Settings {
|
||||
|
||||
Sessions::Sessions(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: Section(parent) {
|
||||
setupContent(controller);
|
||||
}
|
||||
|
||||
void Sessions::setupContent(not_null<Window::SessionController*> controller) {
|
||||
const auto container = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||
const auto content = container->add(
|
||||
object_ptr<SessionsContent>(container, &controller->session()));
|
||||
content->setupContent();
|
||||
|
||||
Ui::ResizeFitChild(this, container);
|
||||
}
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
@@ -8,20 +8,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
class ConfirmBox;
|
||||
|
||||
namespace Ui {
|
||||
class IconButton;
|
||||
class LinkButton;
|
||||
} // namespace Ui
|
||||
#include "settings/settings_common.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Settings {
|
||||
|
||||
class Sessions : public Section {
|
||||
public:
|
||||
Sessions(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller);
|
||||
|
||||
private:
|
||||
void setupContent(not_null<Window::SessionController*> controller);
|
||||
|
||||
};
|
||||
|
||||
} // namespace Settings
|
||||
|
||||
class SessionsBox : public Ui::BoxContent {
|
||||
public:
|
||||
SessionsBox(QWidget*, not_null<Main::Session*> session);
|
||||
@@ -29,46 +36,7 @@ public:
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
uint64 hash = 0;
|
||||
|
||||
bool incomplete = false;
|
||||
TimeId activeTime = 0;
|
||||
int nameWidth, activeWidth, infoWidth, ipWidth;
|
||||
QString name, active, info, ip;
|
||||
};
|
||||
struct Full {
|
||||
Entry current;
|
||||
std::vector<Entry> incomplete;
|
||||
std::vector<Entry> list;
|
||||
};
|
||||
class Inner;
|
||||
class List;
|
||||
|
||||
static Entry ParseEntry(const MTPDauthorization &data);
|
||||
static void ResizeEntry(Entry &entry);
|
||||
void setLoading(bool loading);
|
||||
void shortPollSessions();
|
||||
|
||||
void got(const MTPaccount_Authorizations &result);
|
||||
|
||||
void terminateOne(uint64 hash);
|
||||
void terminateAll();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
MTP::Sender _api;
|
||||
|
||||
bool _loading = false;
|
||||
Full _data;
|
||||
|
||||
QPointer<Inner> _inner;
|
||||
QPointer<ConfirmBox> _terminateBox;
|
||||
|
||||
base::Timer _shortPollTimer;
|
||||
mtpRequestId _shortPollRequest = 0;
|
||||
|
||||
};
|
||||
|
||||
@@ -635,7 +635,11 @@ void ShareBox::Inner::updateChat(not_null<PeerData*> peer) {
|
||||
void ShareBox::Inner::updateChatName(
|
||||
not_null<Chat*> chat,
|
||||
not_null<PeerData*> peer) {
|
||||
const auto text = peer->isSelf() ? tr::lng_saved_messages(tr::now) : peer->name;
|
||||
const auto text = peer->isSelf()
|
||||
? tr::lng_saved_messages(tr::now)
|
||||
: peer->isRepliesChat()
|
||||
? tr::lng_replies_messages(tr::now)
|
||||
: peer->name;
|
||||
chat->name.setText(st::shareNameStyle, text, Ui::NameTextOptions());
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/image/image_location_factory.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/emoji_config.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "lottie/lottie_multi_player.h"
|
||||
#include "lottie/lottie_animation.h"
|
||||
#include "chat_helpers/stickers_lottie.h"
|
||||
@@ -36,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "app.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_info.h"
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
@@ -189,11 +192,10 @@ void StickerSetBox::addStickers() {
|
||||
_inner->install();
|
||||
}
|
||||
|
||||
void StickerSetBox::shareStickers() {
|
||||
void StickerSetBox::copyStickersLink() {
|
||||
const auto url = _controller->session().createInternalLinkFull(
|
||||
qsl("addstickers/") + _inner->shortName());
|
||||
QGuiApplication::clipboard()->setText(url);
|
||||
Ui::show(Box<InformBox>(tr::lng_stickers_copied(tr::now)));
|
||||
}
|
||||
|
||||
void StickerSetBox::updateTitleAndButtons() {
|
||||
@@ -207,10 +209,33 @@ void StickerSetBox::updateButtons() {
|
||||
if (_inner->notInstalled()) {
|
||||
addButton(tr::lng_stickers_add_pack(), [=] { addStickers(); });
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
|
||||
if (!_inner->shortName().isEmpty()) {
|
||||
const auto top = addTopButton(st::infoTopBarMenu);
|
||||
const auto share = [=] {
|
||||
copyStickersLink();
|
||||
Ui::Toast::Show(tr::lng_stickers_copied(tr::now));
|
||||
closeBox();
|
||||
};
|
||||
const auto menu =
|
||||
std::make_shared<base::unique_qptr<Ui::PopupMenu>>();
|
||||
top->setClickedCallback([=] {
|
||||
*menu = base::make_unique_q<Ui::PopupMenu>(top);
|
||||
(*menu)->addAction(
|
||||
tr::lng_stickers_share_pack(tr::now),
|
||||
share);
|
||||
(*menu)->popup(QCursor::pos());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
} else if (_inner->official()) {
|
||||
addButton(tr::lng_about_done(), [=] { closeBox(); });
|
||||
} else {
|
||||
addButton(tr::lng_stickers_share_pack(), [=] { shareStickers(); });
|
||||
auto share = [=] {
|
||||
copyStickersLink();
|
||||
Ui::Toast::Show(tr::lng_stickers_copied(tr::now));
|
||||
};
|
||||
addButton(tr::lng_stickers_share_pack(), std::move(share));
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -41,7 +41,7 @@ private:
|
||||
void updateTitleAndButtons();
|
||||
void updateButtons();
|
||||
void addStickers();
|
||||
void shareStickers();
|
||||
void copyStickersLink();
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
MTPInputStickerSet _set;
|
||||
|
||||
@@ -179,7 +179,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
using SelectedRow = base::optional_variant<MegagroupSet, int>;
|
||||
using SelectedRow = std::variant<v::null_t, MegagroupSet, int>;
|
||||
class AddressField : public Ui::UsernameInput {
|
||||
public:
|
||||
using UsernameInput::UsernameInput;
|
||||
@@ -1101,7 +1101,7 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> row, int index) {
|
||||
|
||||
if (_megagroupSet) {
|
||||
auto selectedIndex = [&] {
|
||||
if (auto index = base::get_if<int>(&_selected)) {
|
||||
if (auto index = std::get_if<int>(&_selected)) {
|
||||
return *index;
|
||||
}
|
||||
return -1;
|
||||
@@ -1341,7 +1341,7 @@ void StickersBox::Inner::mousePressEvent(QMouseEvent *e) {
|
||||
if (_actionSel >= 0) {
|
||||
setActionDown(_actionSel);
|
||||
update(0, _itemsTop + _actionSel * _rowHeight, width(), _rowHeight);
|
||||
} else if (auto selectedIndex = base::get_if<int>(&_selected)) {
|
||||
} else if (auto selectedIndex = std::get_if<int>(&_selected)) {
|
||||
if (_section == Section::Installed && !_rows[*selectedIndex]->isRecentSet() && _inDragArea) {
|
||||
_above = _dragging = _started = *selectedIndex;
|
||||
_dragStart = mapFromGlobal(_mouse);
|
||||
@@ -1394,7 +1394,7 @@ void StickersBox::Inner::setSelected(SelectedRow selected) {
|
||||
return;
|
||||
}
|
||||
auto countSelectedIndex = [&] {
|
||||
if (auto index = base::get_if<int>(&_selected)) {
|
||||
if (auto index = std::get_if<int>(&_selected)) {
|
||||
return *index;
|
||||
}
|
||||
return -1;
|
||||
@@ -1416,7 +1416,7 @@ void StickersBox::Inner::setPressed(SelectedRow pressed) {
|
||||
return;
|
||||
}
|
||||
auto countPressedIndex = [&] {
|
||||
if (auto index = base::get_if<int>(&_pressed)) {
|
||||
if (auto index = std::get_if<int>(&_pressed)) {
|
||||
return *index;
|
||||
}
|
||||
return -1;
|
||||
@@ -1544,7 +1544,7 @@ void StickersBox::Inner::updateCursor() {
|
||||
? ((_actionSel >= 0 && (_actionDown < 0 || _actionDown == _actionSel))
|
||||
? style::cur_pointer
|
||||
: style::cur_default)
|
||||
: (_selected.has_value() || _pressed.has_value())
|
||||
: (!v::is_null(_selected) || !v::is_null(_pressed))
|
||||
? style::cur_pointer
|
||||
: style::cur_default);
|
||||
}
|
||||
@@ -1582,7 +1582,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
_dragging = _started = -1;
|
||||
} else if (pressed == _selected && _actionSel < 0 && _actionDown < 0) {
|
||||
const auto selectedIndex = [&] {
|
||||
if (auto index = base::get_if<int>(&_selected)) {
|
||||
if (auto index = std::get_if<int>(&_selected)) {
|
||||
return *index;
|
||||
}
|
||||
return -1;
|
||||
@@ -1602,7 +1602,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
showSetByRow(*row);
|
||||
}
|
||||
}
|
||||
} else if (_megagroupSelectedSet && _selected.is<MegagroupSet>()) {
|
||||
} else if (_megagroupSelectedSet && v::is<MegagroupSet>(_selected)) {
|
||||
showSetByRow(*_megagroupSelectedSet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,8 +262,8 @@ callReDial: IconButton {
|
||||
width: 40px;
|
||||
height: 56px;
|
||||
|
||||
icon: mainMenuCalls;
|
||||
iconOver: mainMenuCallsOver;
|
||||
icon: icon {{ "call_answer", menuIconFg }};
|
||||
iconOver: icon {{ "call_answer", menuIconFgOver }};
|
||||
iconPosition: point(-1px, -1px);
|
||||
|
||||
ripple: defaultRippleAnimation;
|
||||
@@ -271,6 +271,11 @@ callReDial: IconButton {
|
||||
rippleAreaSize: 40px;
|
||||
}
|
||||
|
||||
callCameraReDial: IconButton(callReDial) {
|
||||
icon: icon {{ "call_camera_active", menuIconFg }};
|
||||
iconOver: icon {{ "call_camera_active", menuIconFgOver }};
|
||||
}
|
||||
|
||||
callRatingPadding: margins(24px, 12px, 24px, 0px);
|
||||
callRatingStar: IconButton {
|
||||
width: 36px;
|
||||
@@ -390,3 +395,7 @@ callTitle: WindowTitle(defaultWindowTitle) {
|
||||
closeIconActiveOver: callTitleCloseIconOver;
|
||||
}
|
||||
callTitleShadow: icon {{ "calls_shadow_controls", windowShadowFg }};
|
||||
|
||||
callErrorToast: Toast(defaultToast) {
|
||||
minWidth: 240px;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@ public:
|
||||
Missed,
|
||||
};
|
||||
|
||||
enum class CallType {
|
||||
Voice,
|
||||
Video,
|
||||
};
|
||||
|
||||
bool canAddItem(not_null<const HistoryItem*> item) const {
|
||||
return (ComputeType(item) == _type)
|
||||
&& (ItemDateTime(item).date() == _date);
|
||||
@@ -90,7 +95,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
QSize actionSize() const override {
|
||||
return peer()->isUser() ? QSize(st::callReDial.width, st::callReDial.height) : QSize();
|
||||
return peer()->isUser() ? QSize(_st->width, _st->height) : QSize();
|
||||
}
|
||||
QMargins actionMargins() const override {
|
||||
return QMargins(
|
||||
@@ -110,10 +115,12 @@ public:
|
||||
private:
|
||||
void refreshStatus() override;
|
||||
static Type ComputeType(not_null<const HistoryItem*> item);
|
||||
static CallType ComputeCallType(not_null<const HistoryItem*> item);
|
||||
|
||||
std::vector<not_null<HistoryItem*>> _items;
|
||||
QDate _date;
|
||||
Type _type;
|
||||
not_null<const style::IconButton*> _st;
|
||||
|
||||
std::unique_ptr<Ui::RippleAnimation> _actionRipple;
|
||||
|
||||
@@ -123,7 +130,10 @@ BoxController::Row::Row(not_null<HistoryItem*> item)
|
||||
: PeerListRow(item->history()->peer, item->id)
|
||||
, _items(1, item)
|
||||
, _date(ItemDateTime(item).date())
|
||||
, _type(ComputeType(item)) {
|
||||
, _type(ComputeType(item))
|
||||
, _st(ComputeCallType(item) == CallType::Voice
|
||||
? &st::callReDial
|
||||
: &st::callCameraReDial) {
|
||||
refreshStatus();
|
||||
}
|
||||
|
||||
@@ -153,12 +163,18 @@ void BoxController::Row::paintAction(
|
||||
bool actionSelected) {
|
||||
auto size = actionSize();
|
||||
if (_actionRipple) {
|
||||
_actionRipple->paint(p, x + st::callReDial.rippleAreaPosition.x(), y + st::callReDial.rippleAreaPosition.y(), outerWidth);
|
||||
_actionRipple->paint(
|
||||
p,
|
||||
x + _st->rippleAreaPosition.x(),
|
||||
y + _st->rippleAreaPosition.y(),
|
||||
outerWidth);
|
||||
if (_actionRipple->empty()) {
|
||||
_actionRipple.reset();
|
||||
}
|
||||
}
|
||||
st::callReDial.icon.paintInCenter(p, style::rtlrect(x, y, size.width(), size.height(), outerWidth));
|
||||
_st->icon.paintInCenter(
|
||||
p,
|
||||
style::rtlrect(x, y, size.width(), size.height(), outerWidth));
|
||||
}
|
||||
|
||||
void BoxController::Row::refreshStatus() {
|
||||
@@ -201,12 +217,28 @@ BoxController::Row::Type BoxController::Row::ComputeType(
|
||||
return Type::In;
|
||||
}
|
||||
|
||||
BoxController::Row::CallType BoxController::Row::ComputeCallType(
|
||||
not_null<const HistoryItem*> item) {
|
||||
if (auto media = item->media()) {
|
||||
if (const auto call = media->call()) {
|
||||
if (call->video) {
|
||||
return CallType::Video;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CallType::Voice;
|
||||
}
|
||||
|
||||
void BoxController::Row::addActionRipple(QPoint point, Fn<void()> updateCallback) {
|
||||
if (!_actionRipple) {
|
||||
auto mask = Ui::RippleAnimation::ellipseMask(QSize(st::callReDial.rippleAreaSize, st::callReDial.rippleAreaSize));
|
||||
_actionRipple = std::make_unique<Ui::RippleAnimation>(st::callReDial.ripple, std::move(mask), std::move(updateCallback));
|
||||
auto mask = Ui::RippleAnimation::ellipseMask(
|
||||
QSize(_st->rippleAreaSize, _st->rippleAreaSize));
|
||||
_actionRipple = std::make_unique<Ui::RippleAnimation>(
|
||||
_st->ripple,
|
||||
std::move(mask),
|
||||
std::move(updateCallback));
|
||||
}
|
||||
_actionRipple->add(point - st::callReDial.rippleAreaPosition);
|
||||
_actionRipple->add(point - _st->rippleAreaPosition);
|
||||
}
|
||||
|
||||
void BoxController::Row::stopLastActionRipple() {
|
||||
@@ -240,8 +272,11 @@ void BoxController::prepare() {
|
||||
}, lifetime());
|
||||
|
||||
session().changes().messageUpdates(
|
||||
Data::MessageUpdate::Flag::CallAdded
|
||||
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
||||
Data::MessageUpdate::Flag::NewAdded
|
||||
) | rpl::filter([=](const Data::MessageUpdate &update) {
|
||||
const auto media = update.item->media();
|
||||
return (media != nullptr) && (media->call() != nullptr);
|
||||
}) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
||||
insertRow(update.item, InsertWay::Prepend);
|
||||
}, lifetime());
|
||||
|
||||
@@ -262,6 +297,7 @@ void BoxController::loadMoreRows() {
|
||||
MTP_inputPeerEmpty(),
|
||||
MTP_string(),
|
||||
MTP_inputUserEmpty(),
|
||||
MTPint(), // top_msg_id
|
||||
MTP_inputMessagesFilterPhoneCalls(MTP_flags(0)),
|
||||
MTP_int(0),
|
||||
MTP_int(0),
|
||||
|
||||
@@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "calls/calls_panel.h"
|
||||
#include "webrtc/webrtc_video_track.h"
|
||||
#include "webrtc/webrtc_media_devices.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_session.h"
|
||||
#include "facades.h"
|
||||
@@ -345,19 +346,34 @@ void Call::setMuted(bool mute) {
|
||||
}
|
||||
|
||||
void Call::setupOutgoingVideo() {
|
||||
static const auto hasDevices = [] {
|
||||
return !Webrtc::GetVideoInputList().empty();
|
||||
};
|
||||
const auto started = _videoOutgoing->state();
|
||||
if (!hasDevices()) {
|
||||
_videoOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
}
|
||||
_videoOutgoing->stateValue(
|
||||
) | rpl::start_with_next([=](Webrtc::VideoState state) {
|
||||
if (_state.current() != State::Established
|
||||
if (state != Webrtc::VideoState::Inactive && !hasDevices()) {
|
||||
_errors.fire({ ErrorType::NoCamera });
|
||||
_videoOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
} else if (_state.current() != State::Established
|
||||
&& state != started
|
||||
&& !_videoCapture) {
|
||||
_errors.fire({ ErrorType::NotStartedCall });
|
||||
_videoOutgoing->setState(started);
|
||||
} else if (state != Webrtc::VideoState::Inactive
|
||||
&& _instance
|
||||
&& !_instance->supportsVideo()) {
|
||||
_errors.fire({ ErrorType::NotVideoCall });
|
||||
_videoOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
} else if (state != Webrtc::VideoState::Inactive) {
|
||||
// Paused not supported right now.
|
||||
#ifndef DESKTOP_APP_DISABLE_WEBRTC_INTEGRATION
|
||||
Assert(state == Webrtc::VideoState::Active);
|
||||
if (!_videoCapture) {
|
||||
_videoCapture = tgcalls::VideoCaptureInterface::Create();
|
||||
_videoCapture = _delegate->getVideoCapture();
|
||||
_videoCapture->setOutput(_videoOutgoing->sink());
|
||||
}
|
||||
if (_instance) {
|
||||
@@ -409,7 +425,9 @@ void Call::redial() {
|
||||
}
|
||||
|
||||
QString Call::getDebugLog() const {
|
||||
return QString::fromStdString(_instance->getDebugInfo());
|
||||
return _instance
|
||||
? QString::fromStdString(_instance->getDebugInfo())
|
||||
: QString();
|
||||
}
|
||||
|
||||
void Call::startWaitingTrack() {
|
||||
@@ -704,6 +722,8 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
|
||||
auto encryptionKeyValue = std::make_shared<std::array<uint8_t, 256>>();
|
||||
memcpy(encryptionKeyValue->data(), _authKey.data(), 256);
|
||||
|
||||
const auto &settings = Core::App().settings();
|
||||
|
||||
const auto weak = base::make_weak(this);
|
||||
tgcalls::Descriptor descriptor = {
|
||||
.config = tgcalls::Config{
|
||||
@@ -720,6 +740,12 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
|
||||
.encryptionKey = tgcalls::EncryptionKey(
|
||||
std::move(encryptionKeyValue),
|
||||
(_type == Type::Outgoing)),
|
||||
.mediaDevicesConfig = tgcalls::MediaDevicesConfig{
|
||||
.audioInputId = settings.callInputDeviceId().toStdString(),
|
||||
.audioOutputId = settings.callOutputDeviceId().toStdString(),
|
||||
.inputVolume = 1.f,//settings.callInputVolume() / 100.f,
|
||||
.outputVolume = 1.f,//settings.callOutputVolume() / 100.f,
|
||||
},
|
||||
.videoCapture = _videoCapture,
|
||||
.stateUpdated = [=](tgcalls::State state) {
|
||||
crl::on_main(weak, [=] {
|
||||
@@ -804,14 +830,6 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
|
||||
}
|
||||
|
||||
raw->setIncomingVideoOutput(_videoIncoming->sink());
|
||||
|
||||
const auto &settings = Core::App().settings();
|
||||
raw->setAudioOutputDevice(
|
||||
settings.callOutputDeviceID().toStdString());
|
||||
raw->setAudioInputDevice(
|
||||
settings.callInputDeviceID().toStdString());
|
||||
raw->setOutputVolume(settings.callOutputVolume() / 100.0f);
|
||||
raw->setInputVolume(settings.callInputVolume() / 100.0f);
|
||||
raw->setAudioOutputDuckingEnabled(settings.callAudioDuckingEnabled());
|
||||
}
|
||||
|
||||
@@ -833,7 +851,9 @@ void Call::handleControllerStateChange(tgcalls::State state) {
|
||||
} break;
|
||||
|
||||
case tgcalls::State::Failed: {
|
||||
auto error = QString::fromStdString(_instance->getLastError());
|
||||
auto error = _instance
|
||||
? QString::fromStdString(_instance->getLastError())
|
||||
: QString();
|
||||
LOG(("Call Info: State changed to Failed, error: %1.").arg(error));
|
||||
handleControllerError(error);
|
||||
} break;
|
||||
@@ -941,16 +961,23 @@ void Call::setState(State state) {
|
||||
}
|
||||
}
|
||||
|
||||
void Call::setCurrentAudioDevice(bool input, std::string deviceID) {
|
||||
void Call::setCurrentAudioDevice(bool input, const QString &deviceId) {
|
||||
if (_instance) {
|
||||
const auto id = deviceId.toStdString();
|
||||
if (input) {
|
||||
_instance->setAudioInputDevice(deviceID);
|
||||
_instance->setAudioInputDevice(id);
|
||||
} else {
|
||||
_instance->setAudioOutputDevice(deviceID);
|
||||
_instance->setAudioOutputDevice(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Call::setCurrentVideoDevice(const QString &deviceId) {
|
||||
if (_videoCapture) {
|
||||
_videoCapture->switchToDevice(deviceId.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
void Call::setAudioVolume(bool input, float level) {
|
||||
if (_instance) {
|
||||
if (input) {
|
||||
@@ -1055,7 +1082,8 @@ void Call::handleControllerError(const QString &error) {
|
||||
|
||||
void Call::destroyController() {
|
||||
if (_instance) {
|
||||
const auto state = _instance->stop();
|
||||
_instance->stop([](tgcalls::FinalState) {
|
||||
});
|
||||
|
||||
DEBUG_LOG(("Call Info: Destroying call controller.."));
|
||||
_instance.reset();
|
||||
|
||||
@@ -40,6 +40,19 @@ struct DhConfig {
|
||||
bytes::vector p;
|
||||
};
|
||||
|
||||
enum class ErrorType {
|
||||
NoCamera,
|
||||
NoMicrophone,
|
||||
NotStartedCall,
|
||||
NotVideoCall,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
struct Error {
|
||||
ErrorType type = ErrorType::Unknown;
|
||||
QString details;
|
||||
};
|
||||
|
||||
class Call : public base::has_weak_ptr {
|
||||
public:
|
||||
class Delegate {
|
||||
@@ -55,7 +68,9 @@ public:
|
||||
Ended,
|
||||
};
|
||||
virtual void playSound(Sound sound) = 0;
|
||||
virtual void requestPermissionsOrFail(Fn<void()> result) = 0;
|
||||
virtual void requestPermissionsOrFail(Fn<void()> onSuccess) = 0;
|
||||
virtual auto getVideoCapture()
|
||||
-> std::shared_ptr<tgcalls::VideoCaptureInterface> = 0;
|
||||
|
||||
virtual ~Delegate() = default;
|
||||
|
||||
@@ -105,6 +120,10 @@ public:
|
||||
return _state.value();
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<Error> errors() const {
|
||||
return _errors.events();
|
||||
}
|
||||
|
||||
enum class RemoteAudioState {
|
||||
Muted,
|
||||
Active,
|
||||
@@ -155,7 +174,8 @@ public:
|
||||
|
||||
QString getDebugLog() const;
|
||||
|
||||
void setCurrentAudioDevice(bool input, std::string deviceID);
|
||||
void setCurrentAudioDevice(bool input, const QString &deviceId);
|
||||
void setCurrentVideoDevice(const QString &deviceId);
|
||||
void setAudioVolume(bool input, float level);
|
||||
void setAudioDuckingEnabled(bool enabled);
|
||||
|
||||
@@ -213,6 +233,7 @@ private:
|
||||
rpl::variable<State> _state = State::Starting;
|
||||
rpl::variable<RemoteAudioState> _remoteAudioState = RemoteAudioState::Active;
|
||||
rpl::variable<Webrtc::VideoState> _remoteVideoState;
|
||||
rpl::event_stream<Error> _errors;
|
||||
FinishType _finishAfterRequestingCall = FinishType::None;
|
||||
bool _answerAfterDhConfigReceived = false;
|
||||
rpl::variable<int> _signalBarCount = kSignalBarStarting;
|
||||
|
||||
@@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mainwidget.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "boxes/rate_call_box.h"
|
||||
#include "tgcalls/VideoCaptureInterface.h"
|
||||
#include "app.h"
|
||||
|
||||
namespace Calls {
|
||||
@@ -344,4 +345,15 @@ void Instance::requestPermissionOrFail(Platform::PermissionType type, Fn<void()>
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<tgcalls::VideoCaptureInterface> Instance::getVideoCapture() {
|
||||
if (auto result = _videoCapture.lock()) {
|
||||
return result;
|
||||
}
|
||||
auto result = std::shared_ptr<tgcalls::VideoCaptureInterface>(
|
||||
tgcalls::VideoCaptureInterface::Create(
|
||||
Core::App().settings().callVideoInputDeviceId().toStdString()));
|
||||
_videoCapture = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Calls
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
void showInfoPanel(not_null<Call*> call);
|
||||
[[nodiscard]] Call *currentCall() const;
|
||||
[[nodiscard]] rpl::producer<Call*> currentCallValue() const;
|
||||
std::shared_ptr<tgcalls::VideoCaptureInterface> getVideoCapture() override;
|
||||
|
||||
[[nodiscard]] bool isQuitPrevent();
|
||||
|
||||
@@ -78,6 +79,7 @@ private:
|
||||
|
||||
crl::time _lastServerConfigUpdateTime = 0;
|
||||
base::weak_ptr<Main::Session> _serverConfigRequestSession;
|
||||
std::weak_ptr<tgcalls::VideoCaptureInterface> _videoCapture;
|
||||
|
||||
std::unique_ptr<Call> _currentCall;
|
||||
rpl::event_stream<Call*> _currentCallChanges;
|
||||
|
||||
@@ -24,9 +24,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/window.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/platform/ui_platform_utility.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/empty_userpic.h"
|
||||
#include "ui/emoji_config.h"
|
||||
#include "core/application.h"
|
||||
@@ -37,7 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "platform/platform_specific.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "window/main_window.h"
|
||||
#include "layout.h"
|
||||
#include "app.h"
|
||||
#include "webrtc/webrtc_video_track.h"
|
||||
#include "styles/style_calls.h"
|
||||
@@ -52,6 +53,123 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
namespace Calls {
|
||||
namespace {
|
||||
|
||||
#if defined Q_OS_MAC && !defined OS_MAC_OLD
|
||||
#define USE_OPENGL_OVERLAY_WIDGET
|
||||
#endif // Q_OS_MAC && !OS_MAC_OLD
|
||||
|
||||
#ifdef USE_OPENGL_OVERLAY_WIDGET
|
||||
using IncomingParent = Ui::RpWidgetWrap<QOpenGLWidget>;
|
||||
#else // USE_OPENGL_OVERLAY_WIDGET
|
||||
using IncomingParent = Ui::RpWidget;
|
||||
#endif // USE_OPENGL_OVERLAY_WIDGET
|
||||
|
||||
} // namespace
|
||||
|
||||
class Panel::Incoming final : public IncomingParent {
|
||||
public:
|
||||
Incoming(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Webrtc::VideoTrack*> track);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
void initBottomShadow();
|
||||
void fillTopShadow(QPainter &p);
|
||||
void fillBottomShadow(QPainter &p);
|
||||
|
||||
const not_null<Webrtc::VideoTrack*> _track;
|
||||
QPixmap _bottomShadow;
|
||||
|
||||
};
|
||||
|
||||
Panel::Incoming::Incoming(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Webrtc::VideoTrack*> track)
|
||||
: IncomingParent(parent)
|
||||
, _track(track) {
|
||||
initBottomShadow();
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}
|
||||
|
||||
void Panel::Incoming::paintEvent(QPaintEvent *e) {
|
||||
QPainter p(this);
|
||||
|
||||
const auto frame = _track->frame(Webrtc::FrameRequest());
|
||||
if (frame.isNull()) {
|
||||
p.fillRect(e->rect(), Qt::black);
|
||||
} else {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.drawImage(rect(), frame);
|
||||
fillBottomShadow(p);
|
||||
fillTopShadow(p);
|
||||
}
|
||||
_track->markFrameShown();
|
||||
}
|
||||
|
||||
void Panel::Incoming::initBottomShadow() {
|
||||
auto image = QImage(
|
||||
QSize(1, st::callBottomShadowSize) * cIntRetinaFactor(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
const auto colorFrom = uint32(0);
|
||||
const auto colorTill = uint32(74);
|
||||
const auto rows = image.height();
|
||||
const auto step = (uint64(colorTill - colorFrom) << 32) / rows;
|
||||
auto accumulated = uint64();
|
||||
auto bytes = image.bits();
|
||||
for (auto y = 0; y != rows; ++y) {
|
||||
accumulated += step;
|
||||
const auto color = (colorFrom + uint32(accumulated >> 32)) << 24;
|
||||
for (auto x = 0; x != image.width(); ++x) {
|
||||
*(reinterpret_cast<uint32*>(bytes) + x) = color;
|
||||
}
|
||||
bytes += image.bytesPerLine();
|
||||
}
|
||||
_bottomShadow = Images::PixmapFast(std::move(image));
|
||||
}
|
||||
|
||||
void Panel::Incoming::fillTopShadow(QPainter &p) {
|
||||
#ifdef Q_OS_WIN
|
||||
const auto width = parentWidget()->width();
|
||||
const auto position = QPoint(width - st::callTitleShadow.width(), 0);
|
||||
const auto shadowArea = QRect(
|
||||
position,
|
||||
st::callTitleShadow.size());
|
||||
const auto fill = shadowArea.intersected(geometry()).translated(-pos());
|
||||
if (fill.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
p.save();
|
||||
p.setClipRect(fill);
|
||||
st::callTitleShadow.paint(p, position - pos(), width);
|
||||
p.restore();
|
||||
#endif // Q_OS_WIN
|
||||
}
|
||||
|
||||
void Panel::Incoming::fillBottomShadow(QPainter &p) {
|
||||
const auto shadowArea = QRect(
|
||||
0,
|
||||
parentWidget()->height() - st::callBottomShadowSize,
|
||||
parentWidget()->width(),
|
||||
st::callBottomShadowSize);
|
||||
const auto fill = shadowArea.intersected(geometry()).translated(-pos());
|
||||
if (fill.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const auto factor = cIntRetinaFactor();
|
||||
p.drawPixmap(
|
||||
fill,
|
||||
_bottomShadow,
|
||||
QRect(
|
||||
0,
|
||||
factor * (fill.y() - shadowArea.translated(-pos()).y()),
|
||||
factor,
|
||||
factor * fill.height()));
|
||||
}
|
||||
|
||||
|
||||
class Panel::Button final : public Ui::RippleButton {
|
||||
public:
|
||||
@@ -299,7 +417,6 @@ Panel::Panel(not_null<Call*> call)
|
||||
initWidget();
|
||||
initControls();
|
||||
initLayout();
|
||||
initBottomShadow();
|
||||
showAndActivate();
|
||||
}
|
||||
|
||||
@@ -318,6 +435,8 @@ void Panel::replaceCall(not_null<Call*> call) {
|
||||
}
|
||||
|
||||
void Panel::initWindow() {
|
||||
_window->setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
_window->setAttribute(Qt::WA_NoSystemBackground);
|
||||
_window->setWindowIcon(
|
||||
QIcon(QPixmap::fromImage(Image::Empty()->original(), Qt::ColorOnly)));
|
||||
_window->setTitle(u" "_q);
|
||||
@@ -459,16 +578,42 @@ void Panel::setIncomingSize(QSize size) {
|
||||
if (_incomingFrameSize == size) {
|
||||
return;
|
||||
}
|
||||
widget()->update(incomingFrameGeometry());
|
||||
_incomingFrameSize = size;
|
||||
widget()->update(incomingFrameGeometry());
|
||||
refreshIncomingGeometry();
|
||||
showControls();
|
||||
}
|
||||
|
||||
void Panel::refreshIncomingGeometry() {
|
||||
Expects(_call != nullptr);
|
||||
Expects(_incoming != nullptr);
|
||||
|
||||
if (_incomingFrameSize.isEmpty()) {
|
||||
_incoming->hide();
|
||||
return;
|
||||
}
|
||||
const auto to = widget()->size();
|
||||
const auto small = _incomingFrameSize.scaled(to, Qt::KeepAspectRatio);
|
||||
const auto big = _incomingFrameSize.scaled(
|
||||
to,
|
||||
Qt::KeepAspectRatioByExpanding);
|
||||
|
||||
// If we cut out no more than 0.33 of the original, let's use expanding.
|
||||
const auto use = ((big.width() * 3 <= to.width() * 4)
|
||||
&& (big.height() * 3 <= to.height() * 4))
|
||||
? big
|
||||
: small;
|
||||
const auto pos = QPoint(
|
||||
(to.width() - use.width()) / 2,
|
||||
(to.height() - use.height()) / 2);
|
||||
_incoming->setGeometry(QRect(pos, use));
|
||||
_incoming->show();
|
||||
}
|
||||
|
||||
void Panel::reinitWithCall(Call *call) {
|
||||
_callLifetime.destroy();
|
||||
_call = call;
|
||||
if (!_call) {
|
||||
_incoming = nullptr;
|
||||
_outgoingVideoBubble = nullptr;
|
||||
return;
|
||||
}
|
||||
@@ -495,6 +640,10 @@ void Panel::reinitWithCall(Call *call) {
|
||||
_outgoingVideoBubble = std::make_unique<VideoBubble>(
|
||||
widget(),
|
||||
_call->videoOutgoing());
|
||||
_incoming = std::make_unique<Incoming>(
|
||||
widget(),
|
||||
_call->videoIncoming());
|
||||
_incoming->hide();
|
||||
|
||||
_call->mutedValue(
|
||||
) | rpl::start_with_next([=](bool mute) {
|
||||
@@ -521,15 +670,14 @@ void Panel::reinitWithCall(Call *call) {
|
||||
_call->videoIncoming()->renderNextFrame(
|
||||
) | rpl::start_with_next([=] {
|
||||
setIncomingSize(_call->videoIncoming()->frame({}).size());
|
||||
if (_incomingFrameSize.isEmpty()) {
|
||||
if (_incoming->isHidden()) {
|
||||
return;
|
||||
}
|
||||
const auto incoming = incomingFrameGeometry();
|
||||
const auto outgoing = outgoingFrameGeometry();
|
||||
_incoming->update();
|
||||
if (incoming.intersects(outgoing)) {
|
||||
widget()->update(incoming.united(outgoing));
|
||||
} else {
|
||||
widget()->update(incoming);
|
||||
widget()->update(outgoing);
|
||||
}
|
||||
}, _callLifetime);
|
||||
|
||||
@@ -537,10 +685,9 @@ void Panel::reinitWithCall(Call *call) {
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto incoming = incomingFrameGeometry();
|
||||
const auto outgoing = outgoingFrameGeometry();
|
||||
widget()->update(outgoing);
|
||||
if (incoming.intersects(outgoing)) {
|
||||
widget()->update(incoming.united(outgoing));
|
||||
} else {
|
||||
widget()->update(outgoing);
|
||||
_incoming->update();
|
||||
}
|
||||
}, _callLifetime);
|
||||
|
||||
@@ -557,8 +704,34 @@ void Panel::reinitWithCall(Call *call) {
|
||||
}
|
||||
}, _callLifetime);
|
||||
|
||||
_call->errors(
|
||||
) | rpl::start_with_next([=](Error error) {
|
||||
const auto text = [=] {
|
||||
switch (error.type) {
|
||||
case ErrorType::NoCamera:
|
||||
return tr::lng_call_error_no_camera(tr::now);
|
||||
case ErrorType::NotVideoCall:
|
||||
return tr::lng_call_error_camera_outdated(tr::now, lt_user, _user->name);
|
||||
case ErrorType::NotStartedCall:
|
||||
return tr::lng_call_error_camera_not_started(tr::now);
|
||||
//case ErrorType::NoMicrophone:
|
||||
// return tr::lng_call_error_no_camera(tr::now);
|
||||
case ErrorType::Unknown:
|
||||
return Lang::Hard::CallErrorIncompatible();
|
||||
}
|
||||
Unexpected("Error type in _call->errors().");
|
||||
}();
|
||||
Ui::Toast::Show(widget(), Ui::Toast::Config{
|
||||
.text = { text },
|
||||
.st = &st::callErrorToast,
|
||||
.multiline = true,
|
||||
});
|
||||
}, _callLifetime);
|
||||
|
||||
_name->setText(_user->name);
|
||||
updateStatusText(_call->state());
|
||||
|
||||
_incoming->lower();
|
||||
}
|
||||
|
||||
void Panel::createRemoteAudioMute() {
|
||||
@@ -615,66 +788,6 @@ void Panel::initLayout() {
|
||||
#endif // Q_OS_WIN
|
||||
}
|
||||
|
||||
void Panel::initBottomShadow() {
|
||||
auto image = QImage(
|
||||
QSize(1, st::callBottomShadowSize) * cIntRetinaFactor(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
const auto colorFrom = uint32(0);
|
||||
const auto colorTill = uint32(74);
|
||||
const auto rows = image.height();
|
||||
const auto step = (uint64(colorTill - colorFrom) << 32) / rows;
|
||||
auto accumulated = uint64();
|
||||
auto bytes = image.bits();
|
||||
for (auto y = 0; y != rows; ++y) {
|
||||
accumulated += step;
|
||||
const auto color = (colorFrom + uint32(accumulated >> 32)) << 24;
|
||||
for (auto x = 0; x != image.width(); ++x) {
|
||||
*(reinterpret_cast<uint32*>(bytes) + x) = color;
|
||||
}
|
||||
bytes += image.bytesPerLine();
|
||||
}
|
||||
_bottomShadow = Images::PixmapFast(std::move(image));
|
||||
}
|
||||
|
||||
void Panel::fillTopShadow(QPainter &p, QRect incoming) {
|
||||
#ifdef Q_OS_WIN
|
||||
const auto width = widget()->width();
|
||||
const auto position = QPoint(width - st::callTitleShadow.width(), 0);
|
||||
const auto shadowArea = QRect(
|
||||
position,
|
||||
st::callTitleShadow.size());
|
||||
const auto fill = shadowArea.intersected(incoming);
|
||||
if (fill.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
p.save();
|
||||
p.setClipRect(fill);
|
||||
st::callTitleShadow.paint(p, position, width);
|
||||
p.restore();
|
||||
#endif // Q_OS_WIN
|
||||
}
|
||||
|
||||
void Panel::fillBottomShadow(QPainter &p, QRect incoming) {
|
||||
const auto shadowArea = QRect(
|
||||
0,
|
||||
widget()->height() - st::callBottomShadowSize,
|
||||
widget()->width(),
|
||||
st::callBottomShadowSize);
|
||||
const auto fill = shadowArea.intersected(incoming);
|
||||
if (fill.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const auto factor = cIntRetinaFactor();
|
||||
p.drawPixmap(
|
||||
fill,
|
||||
_bottomShadow,
|
||||
QRect(
|
||||
0,
|
||||
factor * (fill.y() - shadowArea.y()),
|
||||
factor,
|
||||
factor * fill.height()));
|
||||
}
|
||||
|
||||
void Panel::showControls() {
|
||||
Expects(_call != nullptr);
|
||||
|
||||
@@ -683,6 +796,7 @@ void Panel::showControls() {
|
||||
_cancel->setVisible(_cancel->toggled());
|
||||
|
||||
const auto shown = !_incomingFrameSize.isEmpty();
|
||||
_incoming->setVisible(shown);
|
||||
_name->setVisible(!shown);
|
||||
_status->setVisible(!shown);
|
||||
_userpic->setVisible(!shown);
|
||||
@@ -726,24 +840,9 @@ void Panel::toggleFullScreen(bool fullscreen) {
|
||||
}
|
||||
|
||||
QRect Panel::incomingFrameGeometry() const {
|
||||
if (!_call || _incomingFrameSize.isEmpty()) {
|
||||
return QRect();
|
||||
}
|
||||
const auto to = widget()->size();
|
||||
const auto small = _incomingFrameSize.scaled(to, Qt::KeepAspectRatio);
|
||||
const auto big = _incomingFrameSize.scaled(
|
||||
to,
|
||||
Qt::KeepAspectRatioByExpanding);
|
||||
|
||||
// If we cut out no more than 0.33 of the original, let's use expanding.
|
||||
const auto use = ((big.width() * 3 <= to.width() * 4)
|
||||
&& (big.height() * 3 <= to.height() * 4))
|
||||
? big
|
||||
: small;
|
||||
const auto pos = QPoint(
|
||||
(to.width() - use.width()) / 2,
|
||||
(to.height() - use.height()) / 2);
|
||||
return QRect(pos, use);
|
||||
return (!_incoming || _incoming->isHidden())
|
||||
? QRect()
|
||||
: _incoming->geometry();
|
||||
}
|
||||
|
||||
QRect Panel::outgoingFrameGeometry() const {
|
||||
@@ -754,6 +853,9 @@ void Panel::updateControlsGeometry() {
|
||||
if (widget()->size().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (_incoming) {
|
||||
refreshIncomingGeometry();
|
||||
}
|
||||
if (_fingerprint) {
|
||||
#ifdef Q_OS_WIN
|
||||
const auto minRight = _controls->geometry().width()
|
||||
@@ -875,21 +977,16 @@ void Panel::updateStatusGeometry() {
|
||||
void Panel::paint(QRect clip) {
|
||||
Painter p(widget());
|
||||
|
||||
p.fillRect(clip, st::callBgOpaque);
|
||||
|
||||
const auto incoming = incomingFrameGeometry();
|
||||
if (!incoming.isEmpty()) {
|
||||
Assert(_call != nullptr);
|
||||
const auto frame = _call->videoIncoming()->frame(
|
||||
Webrtc::FrameRequest());
|
||||
if (!frame.isNull()) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.drawImage(incoming, frame);
|
||||
}
|
||||
fillBottomShadow(p, incoming);
|
||||
fillTopShadow(p, incoming);
|
||||
auto region = QRegion(clip);
|
||||
if (!_incoming->isHidden()) {
|
||||
region = region.subtracted(QRegion(_incoming->geometry()));
|
||||
}
|
||||
for (const auto rect : region.rects()) {
|
||||
p.fillRect(rect, st::callBgOpaque);
|
||||
}
|
||||
if (_incoming && _incoming->isHidden()) {
|
||||
_call->videoIncoming()->markFrameShown();
|
||||
}
|
||||
_call->videoIncoming()->markFrameShown();
|
||||
}
|
||||
|
||||
void Panel::handleClose() {
|
||||
@@ -973,7 +1070,7 @@ void Panel::updateStatusText(State state) {
|
||||
auto durationMs = _call->getDurationMs();
|
||||
auto durationSeconds = durationMs / 1000;
|
||||
startDurationUpdateTimer(durationMs);
|
||||
return formatDurationText(durationSeconds);
|
||||
return Ui::FormatDurationText(durationSeconds);
|
||||
}
|
||||
return tr::lng_call_status_ended(tr::now);
|
||||
} break;
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
void closeBeforeDestroy();
|
||||
|
||||
private:
|
||||
class Content;
|
||||
class Incoming;
|
||||
class Button;
|
||||
using State = Call::State;
|
||||
using Type = Call::Type;
|
||||
@@ -75,7 +75,6 @@ private:
|
||||
void reinitWithCall(Call *call);
|
||||
void initLayout();
|
||||
void initGeometry();
|
||||
void initBottomShadow();
|
||||
|
||||
void handleClose();
|
||||
|
||||
@@ -91,8 +90,7 @@ private:
|
||||
void updateStatusText(State state);
|
||||
void startDurationUpdateTimer(crl::time currentDuration);
|
||||
void setIncomingSize(QSize size);
|
||||
void fillTopShadow(QPainter &p, QRect incoming);
|
||||
void fillBottomShadow(QPainter &p, QRect incoming);
|
||||
void refreshIncomingGeometry();
|
||||
|
||||
void refreshOutgoingPreviewInBody(State state);
|
||||
void toggleFullScreen(bool fullscreen);
|
||||
@@ -106,6 +104,7 @@ private:
|
||||
not_null<UserData*> _user;
|
||||
|
||||
const std::unique_ptr<Ui::Window> _window;
|
||||
std::unique_ptr<Incoming> _incoming;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
std::unique_ptr<Ui::Platform::TitleControls> _controls;
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "core/application.h"
|
||||
#include "calls/calls_call.h"
|
||||
@@ -20,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "base/timer.h"
|
||||
#include "layout.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_calls.h"
|
||||
#include "styles/style_layers.h"
|
||||
@@ -162,7 +162,7 @@ void TopBar::updateDurationText() {
|
||||
auto durationMs = _call->getDurationMs();
|
||||
auto durationSeconds = durationMs / 1000;
|
||||
startDurationUpdateTimer(durationMs);
|
||||
_durationLabel->setText(formatDurationText(durationSeconds));
|
||||
_durationLabel->setText(Ui::FormatDurationText(durationSeconds));
|
||||
if (_durationLabel->width() != wasWidth) {
|
||||
updateControlsGeometry();
|
||||
}
|
||||
|
||||
@@ -569,7 +569,7 @@ std::vector<QString> EmojiKeywords::languages() {
|
||||
const auto yieldList = [&](const QStringList &list) {
|
||||
result.insert(end(result), list.begin(), list.end());
|
||||
};
|
||||
yield(Lang::Current().id());
|
||||
yield(Lang::Id());
|
||||
yield(Lang::DefaultLanguageId());
|
||||
yield(Lang::CurrentCloudManager().suggestedLanguage());
|
||||
yield(Platform::SystemLanguage());
|
||||
|
||||
@@ -263,8 +263,8 @@ void Row::paintRadio(Painter &p) {
|
||||
const auto loading = _loading
|
||||
? _loading->computeState()
|
||||
: Ui::RadialState{ 0., 0, FullArcLength };
|
||||
const auto isToggledSet = _state.current().is<Active>();
|
||||
const auto isActiveSet = isToggledSet || _state.current().is<Loading>();
|
||||
const auto isToggledSet = v::is<Active>(_state.current());
|
||||
const auto isActiveSet = isToggledSet || v::is<Loading>(_state.current());
|
||||
const auto toggled = _toggled.value(isToggledSet ? 1. : 0.);
|
||||
const auto active = _active.value(isActiveSet ? 1. : 0.);
|
||||
const auto _st = &st::defaultRadio;
|
||||
@@ -345,7 +345,7 @@ void Row::onStateChanged(State was, StateChangeSource source) {
|
||||
}
|
||||
|
||||
void Row::updateStatusColorOverride() {
|
||||
const auto isToggledSet = _state.current().is<Active>();
|
||||
const auto isToggledSet = v::is<Active>(_state.current());
|
||||
const auto toggled = _toggled.value(isToggledSet ? 1. : 0.);
|
||||
const auto over = showOver();
|
||||
if (toggled == 0. && !over) {
|
||||
@@ -373,7 +373,8 @@ void Row::setupContent(const Set &set) {
|
||||
});
|
||||
}) | rpl::flatten_latest(
|
||||
) | rpl::filter([=](const SetState &state) {
|
||||
return !_state.current().is<Failed>() || !state.is<Available>();
|
||||
return !v::is<Failed>(_state.current())
|
||||
|| !v::is<Available>(state);
|
||||
});
|
||||
|
||||
setupLabels(set);
|
||||
@@ -390,9 +391,10 @@ void Row::setupHandler() {
|
||||
clicks(
|
||||
) | rpl::filter([=] {
|
||||
const auto &state = _state.current();
|
||||
return !_switching && (state.is<Ready>() || state.is<Available>());
|
||||
return !_switching && (v::is<Ready>(state)
|
||||
|| v::is<Available>(state));
|
||||
}) | rpl::start_with_next([=] {
|
||||
if (_state.current().is<Available>()) {
|
||||
if (v::is<Available>(_state.current())) {
|
||||
load();
|
||||
return;
|
||||
}
|
||||
@@ -409,7 +411,7 @@ void Row::setupHandler() {
|
||||
|
||||
_state.value(
|
||||
) | rpl::map([=](const SetState &state) {
|
||||
return state.is<Ready>() || state.is<Available>();
|
||||
return v::is<Ready>(state) || v::is<Available>(state);
|
||||
}) | rpl::start_with_next([=](bool active) {
|
||||
setDisabled(!active);
|
||||
setPointerCursor(active);
|
||||
@@ -463,7 +465,7 @@ void Row::setupPreview(const Set &set) {
|
||||
|
||||
void Row::updateLoadingToFinished() {
|
||||
_loading->update(
|
||||
_state.current().is<Failed>() ? 0. : 1.,
|
||||
v::is<Failed>(_state.current()) ? 0. : 1.,
|
||||
true,
|
||||
crl::now());
|
||||
}
|
||||
@@ -471,7 +473,7 @@ void Row::updateLoadingToFinished() {
|
||||
void Row::radialAnimationCallback(crl::time now) {
|
||||
const auto updated = [&] {
|
||||
const auto state = _state.current();
|
||||
if (const auto loading = base::get_if<Loading>(&state)) {
|
||||
if (const auto loading = std::get_if<Loading>(&state)) {
|
||||
return _loading->update(CountProgress(loading), false, now);
|
||||
} else {
|
||||
updateLoadingToFinished();
|
||||
@@ -493,7 +495,7 @@ void Row::setupAnimation() {
|
||||
|
||||
_state.value(
|
||||
) | rpl::map(
|
||||
_1 == Active()
|
||||
_1 == SetState{ Active() }
|
||||
) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
_toggled.start(
|
||||
@@ -505,7 +507,7 @@ void Row::setupAnimation() {
|
||||
|
||||
_state.value(
|
||||
) | rpl::map([](const SetState &state) {
|
||||
return state.is<Loading>() || state.is<Active>();
|
||||
return v::is<Loading>(state) || v::is<Active>(state);
|
||||
}) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](bool active) {
|
||||
_active.start(
|
||||
@@ -517,7 +519,7 @@ void Row::setupAnimation() {
|
||||
|
||||
_state.value(
|
||||
) | rpl::map([](const SetState &state) {
|
||||
return base::get_if<Loading>(&state);
|
||||
return std::get_if<Loading>(&state);
|
||||
}) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](const Loading *loading) {
|
||||
if (loading && !_loading) {
|
||||
|
||||
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "chat_helpers/gifs_list_widget.h"
|
||||
|
||||
#include "apiwrap.h" // ApiWrap::toggleSavedGif
|
||||
#include "api/api_toggling_media.h" // Api::ToggleSavedGif
|
||||
#include "base/const_string.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_document.h"
|
||||
@@ -48,7 +48,7 @@ constexpr auto kSearchBotUsername = "gif"_cs;
|
||||
|
||||
void DeleteSavedGif(not_null<DocumentData*> document) {
|
||||
auto &data = document->owner();
|
||||
document->session().api().toggleSavedGif(
|
||||
Api::ToggleSavedGif(
|
||||
document,
|
||||
Data::FileOriginSavedGifs(),
|
||||
false);
|
||||
|
||||
@@ -150,13 +150,26 @@ void EditLinkBox::prepare() {
|
||||
session);
|
||||
InitSpellchecker(_controller, text);
|
||||
|
||||
const auto url = content->add(
|
||||
object_ptr<Ui::InputField>(
|
||||
const auto placeholder = content->add(
|
||||
object_ptr<Ui::RpWidget>(content),
|
||||
st::markdownLinkFieldPadding);
|
||||
placeholder->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
const auto url = Ui::AttachParentChild(
|
||||
content,
|
||||
object_ptr<Ui::MaskedInputField>(
|
||||
content,
|
||||
st::defaultInputField,
|
||||
tr::lng_formatting_link_url(),
|
||||
_startLink.trimmed()),
|
||||
st::markdownLinkFieldPadding);
|
||||
_startLink.trimmed()));
|
||||
url->heightValue(
|
||||
) | rpl::start_with_next([placeholder](int height) {
|
||||
placeholder->resize(placeholder->width(), height);
|
||||
}, placeholder->lifetime());
|
||||
placeholder->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
url->resize(width, url->height());
|
||||
}, placeholder->lifetime());
|
||||
url->move(placeholder->pos());
|
||||
|
||||
const auto submit = [=] {
|
||||
const auto linkText = text->getLastText();
|
||||
@@ -178,7 +191,7 @@ void EditLinkBox::prepare() {
|
||||
connect(text, &Ui::InputField::submitted, [=] {
|
||||
url->setFocusFast();
|
||||
});
|
||||
connect(url, &Ui::InputField::submitted, [=] {
|
||||
connect(url, &Ui::MaskedInputField::submitted, [=] {
|
||||
if (text->getLastText().isEmpty()) {
|
||||
text->setFocusFast();
|
||||
} else {
|
||||
@@ -198,7 +211,11 @@ void EditLinkBox::prepare() {
|
||||
setDimensions(st::boxWidth, content->height());
|
||||
|
||||
_setInnerFocus = [=] {
|
||||
(_startText.isEmpty() ? text : url)->setFocusFast();
|
||||
if (_startText.isEmpty()) {
|
||||
text->setFocusFast();
|
||||
} else {
|
||||
url->setFocusFast();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -366,7 +366,7 @@ std::vector<int> DefaultLanguages() {
|
||||
append(method->locale());
|
||||
}
|
||||
append(QLocale(Platform::SystemLanguage()));
|
||||
append(QLocale(Lang::LanguageIdOrDefault(Lang::Current().id())));
|
||||
append(QLocale(Lang::LanguageIdOrDefault(Lang::Id())));
|
||||
|
||||
return langs;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_toggling_media.h" // Api::ToggleFavedSticker
|
||||
#include "app.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_window.h"
|
||||
@@ -142,7 +143,7 @@ private:
|
||||
Search,
|
||||
Settings,
|
||||
};
|
||||
using OverState = base::variant<SpecialOver, int>;
|
||||
using OverState = std::variant<SpecialOver, int>;
|
||||
|
||||
template <typename Callback>
|
||||
void enumerateVisibleIcons(Callback callback);
|
||||
@@ -547,7 +548,7 @@ void StickersListWidget::Footer::mouseMoveEvent(QMouseEvent *e) {
|
||||
|
||||
if (!_iconsDragging
|
||||
&& !_icons.empty()
|
||||
&& base::get_if<int>(&_iconDown) != nullptr) {
|
||||
&& v::is<int>(_iconDown)) {
|
||||
if ((_iconsMousePos - _iconsMouseDown).manhattanLength() >= QApplication::startDragDistance()) {
|
||||
_iconsDragging = true;
|
||||
}
|
||||
@@ -578,7 +579,7 @@ void StickersListWidget::Footer::mouseReleaseEvent(QMouseEvent *e) {
|
||||
|
||||
updateSelected();
|
||||
if (wasDown == _iconOver) {
|
||||
if (const auto index = base::get_if<int>(&_iconOver)) {
|
||||
if (const auto index = std::get_if<int>(&_iconOver)) {
|
||||
_iconSelX = anim::value(
|
||||
*index * st::stickerIconWidth,
|
||||
*index * st::stickerIconWidth);
|
||||
@@ -603,7 +604,7 @@ bool StickersListWidget::Footer::eventHook(QEvent *e) {
|
||||
if (e->type() == QEvent::TouchBegin) {
|
||||
} else if (e->type() == QEvent::Wheel) {
|
||||
if (!_icons.empty()
|
||||
&& (base::get_if<int>(&_iconOver) != nullptr)
|
||||
&& v::is<int>(_iconOver)
|
||||
&& (_iconDown == SpecialOver::None)) {
|
||||
scrollByWheelEvent(static_cast<QWheelEvent*>(e));
|
||||
}
|
||||
@@ -634,7 +635,7 @@ void StickersListWidget::Footer::scrollByWheelEvent(
|
||||
}
|
||||
|
||||
void StickersListWidget::Footer::updateSelected() {
|
||||
if (_iconDown >= 0) {
|
||||
if (_iconDown != SpecialOver::None) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1508,8 +1509,10 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
|
||||
}
|
||||
|
||||
auto &sets = shownSets();
|
||||
auto selectedSticker = base::get_if<OverSticker>(&_selected);
|
||||
auto selectedButton = base::get_if<OverButton>(_pressed ? &_pressed : &_selected);
|
||||
auto selectedSticker = std::get_if<OverSticker>(&_selected);
|
||||
auto selectedButton = std::get_if<OverButton>(!v::is_null(_pressed)
|
||||
? &_pressed
|
||||
: &_selected);
|
||||
|
||||
if (sets.empty() && _section == Section::Search) {
|
||||
paintEmptySearchResults(p);
|
||||
@@ -1624,7 +1627,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
|
||||
if (clip.top() + clip.height() <= info.rowsTop) {
|
||||
return true;
|
||||
} else if (set.id == Data::Stickers::MegagroupSetId && set.stickers.empty()) {
|
||||
auto buttonSelected = (base::get_if<OverGroupAdd>(&_selected) != nullptr);
|
||||
auto buttonSelected = (std::get_if<OverGroupAdd>(&_selected) != nullptr);
|
||||
paintMegagroupEmptySet(p, info.rowsTop, buttonSelected);
|
||||
return true;
|
||||
}
|
||||
@@ -1970,20 +1973,20 @@ void StickersListWidget::mousePressEvent(QMouseEvent *e) {
|
||||
}
|
||||
|
||||
void StickersListWidget::setPressed(OverState newPressed) {
|
||||
if (auto button = base::get_if<OverButton>(&_pressed)) {
|
||||
if (auto button = std::get_if<OverButton>(&_pressed)) {
|
||||
auto &sets = shownSets();
|
||||
Assert(button->section >= 0 && button->section < sets.size());
|
||||
auto &set = sets[button->section];
|
||||
if (set.ripple) {
|
||||
set.ripple->lastStop();
|
||||
}
|
||||
} else if (base::get_if<OverGroupAdd>(&_pressed)) {
|
||||
} else if (std::get_if<OverGroupAdd>(&_pressed)) {
|
||||
if (_megagroupSetButtonRipple) {
|
||||
_megagroupSetButtonRipple->lastStop();
|
||||
}
|
||||
}
|
||||
_pressed = newPressed;
|
||||
if (auto button = base::get_if<OverButton>(&_pressed)) {
|
||||
if (auto button = std::get_if<OverButton>(&_pressed)) {
|
||||
auto &sets = shownSets();
|
||||
Assert(button->section >= 0 && button->section < sets.size());
|
||||
auto &set = sets[button->section];
|
||||
@@ -1991,7 +1994,7 @@ void StickersListWidget::setPressed(OverState newPressed) {
|
||||
set.ripple = createButtonRipple(button->section);
|
||||
}
|
||||
set.ripple->add(mapFromGlobal(QCursor::pos()) - buttonRippleTopLeft(button->section));
|
||||
} else if (base::get_if<OverGroupAdd>(&_pressed)) {
|
||||
} else if (std::get_if<OverGroupAdd>(&_pressed)) {
|
||||
if (!_megagroupSetButtonRipple) {
|
||||
auto maskSize = _megagroupSetButtonRect.size();
|
||||
auto mask = Ui::RippleAnimation::roundRectMask(maskSize, st::buttonRadius);
|
||||
@@ -2058,10 +2061,10 @@ void StickersListWidget::fillContextMenu(
|
||||
SendMenu::Type type) {
|
||||
auto selected = _selected;
|
||||
auto &sets = shownSets();
|
||||
if (!selected || _pressed) {
|
||||
if (v::is_null(selected) || !v::is_null(_pressed)) {
|
||||
return;
|
||||
}
|
||||
if (auto sticker = base::get_if<OverSticker>(&selected)) {
|
||||
if (auto sticker = std::get_if<OverSticker>(&selected)) {
|
||||
Assert(sticker->section >= 0 && sticker->section < sets.size());
|
||||
auto &set = sets[sticker->section];
|
||||
Assert(sticker->index >= 0 && sticker->index < set.stickers.size());
|
||||
@@ -2079,10 +2082,9 @@ void StickersListWidget::fillContextMenu(
|
||||
SendMenu::DefaultScheduleCallback(this, type, send));
|
||||
|
||||
const auto toggleFavedSticker = [=] {
|
||||
document->session().api().toggleFavedSticker(
|
||||
Api::ToggleFavedSticker(
|
||||
document,
|
||||
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0),
|
||||
!document->owner().stickers().isFaved(document));
|
||||
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0));
|
||||
};
|
||||
menu->addAction(
|
||||
(document->owner().stickers().isFaved(document)
|
||||
@@ -2093,6 +2095,15 @@ void StickersListWidget::fillContextMenu(
|
||||
menu->addAction(tr::lng_context_pack_info(tr::now), [=] {
|
||||
showStickerSetBox(document);
|
||||
});
|
||||
|
||||
if (const auto id = set.id; id == Data::Stickers::RecentSetId) {
|
||||
menu->addAction(tr::lng_recent_stickers_remove(tr::now), [=] {
|
||||
Api::ToggleRecentSticker(
|
||||
document,
|
||||
Data::FileOriginStickerSet(id, 0),
|
||||
false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2100,7 +2111,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
_previewTimer.cancel();
|
||||
|
||||
auto pressed = _pressed;
|
||||
setPressed(std::nullopt);
|
||||
setPressed(v::null);
|
||||
if (pressed != _selected) {
|
||||
update();
|
||||
}
|
||||
@@ -2115,8 +2126,8 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
updateSelected();
|
||||
|
||||
auto &sets = shownSets();
|
||||
if (pressed && pressed == _selected) {
|
||||
if (auto sticker = base::get_if<OverSticker>(&pressed)) {
|
||||
if (!v::is_null(pressed) && pressed == _selected) {
|
||||
if (auto sticker = std::get_if<OverSticker>(&pressed)) {
|
||||
Assert(sticker->section >= 0 && sticker->section < sets.size());
|
||||
auto &set = sets[sticker->section];
|
||||
Assert(sticker->index >= 0 && sticker->index < set.stickers.size());
|
||||
@@ -2136,10 +2147,10 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
} else {
|
||||
_chosen.fire_copy({ .document = document });
|
||||
}
|
||||
} else if (auto set = base::get_if<OverSet>(&pressed)) {
|
||||
} else if (auto set = std::get_if<OverSet>(&pressed)) {
|
||||
Assert(set->section >= 0 && set->section < sets.size());
|
||||
displaySet(sets[set->section].id);
|
||||
} else if (auto button = base::get_if<OverButton>(&pressed)) {
|
||||
} else if (auto button = std::get_if<OverButton>(&pressed)) {
|
||||
Assert(button->section >= 0 && button->section < sets.size());
|
||||
if (sets[button->section].externalLayout) {
|
||||
installSet(sets[button->section].id);
|
||||
@@ -2150,7 +2161,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
} else {
|
||||
removeSet(sets[button->section].id);
|
||||
}
|
||||
} else if (base::get_if<OverGroupAdd>(&pressed)) {
|
||||
} else if (std::get_if<OverGroupAdd>(&pressed)) {
|
||||
Ui::show(Box<StickersBox>(controller(), _megagroupSet));
|
||||
}
|
||||
}
|
||||
@@ -2219,7 +2230,7 @@ void StickersListWidget::removeFavedSticker(int section, int index) {
|
||||
const auto &sticker = _mySets[section].stickers[index];
|
||||
const auto document = sticker.document;
|
||||
session().data().stickers().setFaved(document, false);
|
||||
session().api().toggleFavedSticker(
|
||||
Api::ToggleFavedSticker(
|
||||
document,
|
||||
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0),
|
||||
false);
|
||||
@@ -2262,8 +2273,8 @@ void StickersListWidget::enterFromChildEvent(QEvent *e, QWidget *child) {
|
||||
}
|
||||
|
||||
void StickersListWidget::clearSelection() {
|
||||
setPressed(std::nullopt);
|
||||
setSelected(std::nullopt);
|
||||
setPressed(v::null);
|
||||
setSelected(v::null);
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -2741,11 +2752,11 @@ bool StickersListWidget::preventAutoHide() {
|
||||
}
|
||||
|
||||
void StickersListWidget::updateSelected() {
|
||||
if (_pressed && !_previewShown) {
|
||||
if (!v::is_null(_pressed) && !_previewShown) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto newSelected = OverState { std::nullopt };
|
||||
auto newSelected = OverState { v::null };
|
||||
auto p = mapFromGlobal(_lastMousePosition);
|
||||
if (!rect().contains(p)
|
||||
|| p.y() < getVisibleTop() || p.y() >= getVisibleBottom()
|
||||
@@ -2818,13 +2829,15 @@ bool StickersListWidget::stickerHasDeleteButton(const Set &set, int index) const
|
||||
|
||||
void StickersListWidget::setSelected(OverState newSelected) {
|
||||
if (_selected != newSelected) {
|
||||
setCursor(newSelected ? style::cur_pointer : style::cur_default);
|
||||
setCursor(!v::is_null(newSelected)
|
||||
? style::cur_pointer
|
||||
: style::cur_default);
|
||||
|
||||
auto &sets = shownSets();
|
||||
auto updateSelected = [&]() {
|
||||
if (auto sticker = base::get_if<OverSticker>(&_selected)) {
|
||||
if (auto sticker = std::get_if<OverSticker>(&_selected)) {
|
||||
rtlupdate(stickerRect(sticker->section, sticker->index));
|
||||
} else if (auto button = base::get_if<OverButton>(&_selected)) {
|
||||
} else if (auto button = std::get_if<OverButton>(&_selected)) {
|
||||
if (button->section >= 0
|
||||
&& button->section < sets.size()
|
||||
&& sets[button->section].externalLayout) {
|
||||
@@ -2832,7 +2845,7 @@ void StickersListWidget::setSelected(OverState newSelected) {
|
||||
} else {
|
||||
rtlupdate(removeButtonRect(button->section));
|
||||
}
|
||||
} else if (base::get_if<OverGroupAdd>(&_selected)) {
|
||||
} else if (std::get_if<OverGroupAdd>(&_selected)) {
|
||||
rtlupdate(megagroupSetButtonRectFinal());
|
||||
}
|
||||
};
|
||||
@@ -2841,7 +2854,7 @@ void StickersListWidget::setSelected(OverState newSelected) {
|
||||
updateSelected();
|
||||
|
||||
if (_previewShown && _pressed != _selected) {
|
||||
if (const auto sticker = base::get_if<OverSticker>(&_selected)) {
|
||||
if (const auto sticker = std::get_if<OverSticker>(&_selected)) {
|
||||
_pressed = _selected;
|
||||
Assert(sticker->section >= 0 && sticker->section < sets.size());
|
||||
const auto &set = sets[sticker->section];
|
||||
@@ -2856,7 +2869,7 @@ void StickersListWidget::setSelected(OverState newSelected) {
|
||||
}
|
||||
|
||||
void StickersListWidget::showPreview() {
|
||||
if (const auto sticker = base::get_if<OverSticker>(&_pressed)) {
|
||||
if (const auto sticker = std::get_if<OverSticker>(&_pressed)) {
|
||||
const auto &sets = shownSets();
|
||||
Assert(sticker->section >= 0 && sticker->section < sets.size());
|
||||
const auto &set = sets[sticker->section];
|
||||
|
||||