Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60e43cfa3f | ||
|
|
46885b7f9f | ||
|
|
fff42a664c | ||
|
|
437d35d8c4 | ||
|
|
36a5dd8d8b | ||
|
|
d8e99b4860 | ||
|
|
3cddcaa039 | ||
|
|
49078e5679 | ||
|
|
1dec054766 | ||
|
|
91ef6f13c8 | ||
|
|
73c8f16340 | ||
|
|
cbad2469db | ||
|
|
0ae260c6e1 | ||
|
|
221ded6d54 | ||
|
|
6f80811ecd | ||
|
|
5bd73bab9b | ||
|
|
ac86f3e5bd | ||
|
|
bae6a29326 | ||
|
|
dcf86f55af | ||
|
|
2d223b3a2d | ||
|
|
f3ab01604c | ||
|
|
66bcc20f58 | ||
|
|
d69a1d3cd9 | ||
|
|
ccc5aeb8f7 | ||
|
|
5899d60363 | ||
|
|
4f89216db0 | ||
|
|
bd78bac4bf | ||
|
|
b2e829904f | ||
|
|
256546071b | ||
|
|
c100055fac | ||
|
|
ae30366cbf | ||
|
|
663c99cc2d | ||
|
|
d986e70a89 | ||
|
|
1ebf27bfa1 | ||
|
|
4ef2d3b957 | ||
|
|
868c494299 | ||
|
|
4a86b172d4 | ||
|
|
f71ce9afdf | ||
|
|
c170a86189 | ||
|
|
116a768fde | ||
|
|
22e77bf3af | ||
|
|
bbbcd37b8f | ||
|
|
bc707320f8 | ||
|
|
000911ecfa | ||
|
|
479611f6df | ||
|
|
dcc8a64d37 | ||
|
|
a030907598 | ||
|
|
34cac3092f | ||
|
|
05f1caf944 | ||
|
|
93bcd90fd4 | ||
|
|
7c8b1cd5b1 | ||
|
|
82165bec5e | ||
|
|
65aecf16a6 | ||
|
|
ca83b8a8c6 | ||
|
|
024bb5e54f | ||
|
|
a14f2144e1 | ||
|
|
c6071d1148 | ||
|
|
6e2f8eb9a7 | ||
|
|
793f748d2e | ||
|
|
bb404a38d3 | ||
|
|
c82006c6f8 | ||
|
|
2256482ae0 | ||
|
|
d11e756381 | ||
|
|
a128c16f59 | ||
|
|
48207102ce | ||
|
|
2d354f8777 | ||
|
|
b27dc4ef8d |
49
.github/workflows/win.yml
vendored
@@ -52,28 +52,34 @@ jobs:
|
||||
- ""
|
||||
env:
|
||||
SDK: "10.0.18362.0"
|
||||
VC: "call vcvars32.bat && cd Libraries"
|
||||
GIT: "https://github.com"
|
||||
QT: "5_15_2"
|
||||
QT_VER: "5.15.2"
|
||||
OPENSSL_VER: "1_1_1"
|
||||
UPLOAD_ARTIFACT: "false"
|
||||
ONLY_CACHE: "false"
|
||||
MANUAL_CACHING: "2"
|
||||
MANUAL_CACHING: "0"
|
||||
DOC_PATH: "docs/building-win.md"
|
||||
AUTO_CACHING: "1"
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: cmd
|
||||
working-directory: Libraries
|
||||
|
||||
steps:
|
||||
- name: Get repository name.
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
|
||||
|
||||
- uses: ilammy/msvc-dev-cmd@v1.9.0
|
||||
name: x86 Native Tools Command Prompt.
|
||||
with:
|
||||
arch: win32
|
||||
|
||||
- name: Set up environment paths.
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
echo "C:\\Strawberry\\perl\\bin\\" >> $GITHUB_PATH
|
||||
echo "C:\\Program Files\\NASM\\" >> $GITHUB_PATH
|
||||
@@ -84,6 +90,8 @@ jobs:
|
||||
p=`pwd | sed 's#^/[d]#d:#g' |sed 's#/#\\\\#g'`
|
||||
echo "LibrariesPath=$p" >> $GITHUB_ENV
|
||||
|
||||
echo "QT=${QT_VER//./_}" >> $GITHUB_ENV
|
||||
|
||||
- name: Save msbuild version.
|
||||
run: |
|
||||
call vcvars32.bat
|
||||
@@ -97,6 +105,7 @@ jobs:
|
||||
|
||||
- name: Generate cache key.
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
curl -o $LibrariesPath/tg_owt-version.json https://api.github.com/repos/desktop-app/tg_owt/git/refs/heads/master
|
||||
curl -o $LibrariesPath/tg_angle-version.json https://api.github.com/repos/desktop-app/tg_angle/git/refs/heads/master
|
||||
@@ -108,7 +117,10 @@ jobs:
|
||||
echo "CACHE_KEY=`md5sum CACHE_KEY.txt | awk '{ print $1 }'`" >> $GITHUB_ENV
|
||||
|
||||
- name: Choco installs.
|
||||
run: choco install --no-progress -y nasm yasm jom ninja
|
||||
run: |
|
||||
choco install --allow-empty-checksums --no-progress -y yasm
|
||||
choco install --no-progress -y nasm jom ninja
|
||||
python -m pip install pywin32
|
||||
|
||||
- name: NuGet sources.
|
||||
run: |
|
||||
@@ -117,6 +129,7 @@ jobs:
|
||||
|
||||
- name: Patches.
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
echo "Find necessary commit from doc."
|
||||
checkoutCommit=$(grep -A 1 "cd patches" $REPO_NAME/$DOC_PATH | sed -n 2p)
|
||||
@@ -139,8 +152,6 @@ jobs:
|
||||
|
||||
- name: LZMA.
|
||||
run: |
|
||||
%VC%
|
||||
|
||||
git clone %GIT%/telegramdesktop/lzma.git
|
||||
cd lzma
|
||||
cd C\Util\LzmaLib
|
||||
@@ -155,8 +166,6 @@ jobs:
|
||||
- name: OpenSSL.
|
||||
if: steps.cache-openssl.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
%VC%
|
||||
|
||||
git clone %GIT%/openssl/openssl.git openssl_%OPENSSL_VER%
|
||||
cd openssl_%OPENSSL_VER%
|
||||
git checkout OpenSSL_%OPENSSL_VER%-stable
|
||||
@@ -180,8 +189,6 @@ jobs:
|
||||
|
||||
- name: Zlib.
|
||||
run: |
|
||||
%VC%
|
||||
|
||||
git clone %GIT%/telegramdesktop/zlib.git
|
||||
cd zlib
|
||||
git checkout tdesktop
|
||||
@@ -191,8 +198,6 @@ jobs:
|
||||
- name: MozJPEG.
|
||||
shell: cmd
|
||||
run: |
|
||||
%VC%
|
||||
|
||||
git clone -b v4.0.1-rc2 %GIT%/mozilla/mozjpeg.git
|
||||
cd mozjpeg
|
||||
cmake . ^
|
||||
@@ -211,8 +216,6 @@ jobs:
|
||||
- name: OpenAL Soft.
|
||||
if: steps.cache-openal.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
%VC%
|
||||
|
||||
git clone -b openal-soft-1.21.0 --depth=1 %GIT%/kcat/openal-soft.git
|
||||
cd openal-soft\build
|
||||
cmake .. ^
|
||||
@@ -236,8 +239,6 @@ jobs:
|
||||
GYP_MSVS_VERSION: 2019
|
||||
if: steps.cache-breakpad.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd %LibrariesPath%
|
||||
|
||||
git clone %GIT%/telegramdesktop/gyp.git
|
||||
cd gyp
|
||||
SET PATH=%PY2%;%cd%;%PATH%
|
||||
@@ -271,8 +272,6 @@ jobs:
|
||||
- name: Opus.
|
||||
if: steps.cache-opus.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
%VC%
|
||||
|
||||
git clone %GIT%/telegramdesktop/opus.git
|
||||
cd opus
|
||||
git checkout tdesktop
|
||||
@@ -281,10 +280,7 @@ jobs:
|
||||
msbuild -m opus.sln /property:Configuration=Release /property:Platform="Win32"
|
||||
|
||||
- name: Rnnoise.
|
||||
shell: cmd
|
||||
run: |
|
||||
%VC%
|
||||
|
||||
git clone %GIT%/desktop-app/rnnoise.git
|
||||
mkdir rnnoise\out
|
||||
cd rnnoise\out
|
||||
@@ -300,7 +296,6 @@ jobs:
|
||||
- name: FFmpeg.
|
||||
if: steps.cache-ffmpeg.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
%VC%
|
||||
choco install --no-progress -y msys2
|
||||
|
||||
git clone %GIT%/FFmpeg/FFmpeg.git ffmpeg
|
||||
@@ -321,8 +316,6 @@ jobs:
|
||||
- name: Angle.
|
||||
if: steps.cache-angle.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
%VC%
|
||||
|
||||
git clone --recursive %GIT%/desktop-app/tg_angle.git
|
||||
mkdir tg_angle\out\Debug
|
||||
cd tg_angle\out\Debug
|
||||
@@ -348,8 +341,6 @@ jobs:
|
||||
- name: Configure Qt 5.15.2.
|
||||
if: steps.cache-qt.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
%VC%
|
||||
|
||||
git clone git://code.qt.io/qt/qt5.git qt_%QT%
|
||||
cd qt_%QT%
|
||||
perl init-repository --module-subset=qtbase,qtimageformats
|
||||
@@ -401,7 +392,6 @@ jobs:
|
||||
- name: Qt 5.15.2 build.
|
||||
if: steps.cache-qt.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
%VC%
|
||||
cd qt_%QT%
|
||||
|
||||
jom -j%NUMBER_OF_PROCESSORS%
|
||||
@@ -419,8 +409,6 @@ jobs:
|
||||
- name: WebRTC.
|
||||
if: steps.cache-webrtc.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
%VC%
|
||||
|
||||
git clone --recursive %GIT%/desktop-app/tg_owt.git
|
||||
mkdir tg_owt\out\Debug
|
||||
cd tg_owt\out\Debug
|
||||
@@ -457,10 +445,12 @@ jobs:
|
||||
echo "TDESKTOP_BUILD_DEFINE=$DEFINE" >> $GITHUB_ENV
|
||||
|
||||
- name: Free up some disk space.
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: del /S *.pdb
|
||||
|
||||
- name: Telegram Desktop build.
|
||||
if: env.ONLY_CACHE == 'false'
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
cd %REPO_NAME%\Telegram
|
||||
|
||||
@@ -478,6 +468,7 @@ jobs:
|
||||
|
||||
- name: Move artifact.
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
cd %REPO_NAME%\out\Debug
|
||||
mkdir artifact
|
||||
|
||||
3
.gitmodules
vendored
@@ -91,3 +91,6 @@
|
||||
[submodule "Telegram/lib_waylandshells"]
|
||||
path = Telegram/lib_waylandshells
|
||||
url = https://github.com/desktop-app/lib_waylandshells.git
|
||||
[submodule "Telegram/ThirdParty/jemalloc"]
|
||||
path = Telegram/ThirdParty/jemalloc
|
||||
url = https://github.com/jemalloc/jemalloc
|
||||
|
||||
@@ -91,12 +91,16 @@ PRIVATE
|
||||
api/api_attached_stickers.h
|
||||
api/api_authorizations.cpp
|
||||
api/api_authorizations.h
|
||||
api/api_blocked_peers.cpp
|
||||
api/api_blocked_peers.h
|
||||
api/api_bot.cpp
|
||||
api/api_bot.h
|
||||
api/api_chat_filters.cpp
|
||||
api/api_chat_filters.h
|
||||
api/api_chat_invite.cpp
|
||||
api/api_chat_invite.h
|
||||
api/api_cloud_password.cpp
|
||||
api/api_cloud_password.h
|
||||
api/api_common.h
|
||||
api/api_editing.cpp
|
||||
api/api_editing.h
|
||||
@@ -124,6 +128,8 @@ PRIVATE
|
||||
api/api_toggling_media.h
|
||||
api/api_updates.cpp
|
||||
api/api_updates.h
|
||||
api/api_user_privacy.cpp
|
||||
api/api_user_privacy.h
|
||||
boxes/filters/edit_filter_box.cpp
|
||||
boxes/filters/edit_filter_box.h
|
||||
boxes/filters/edit_filter_chats_list.cpp
|
||||
@@ -220,6 +226,7 @@ PRIVATE
|
||||
calls/group/calls_choose_join_as.h
|
||||
calls/group/calls_group_call.cpp
|
||||
calls/group/calls_group_call.h
|
||||
calls/group/calls_group_common.cpp
|
||||
calls/group/calls_group_common.h
|
||||
calls/group/calls_group_invite_controller.cpp
|
||||
calls/group/calls_group_invite_controller.h
|
||||
@@ -265,6 +272,8 @@ PRIVATE
|
||||
calls/calls_video_bubble.h
|
||||
calls/calls_video_incoming.cpp
|
||||
calls/calls_video_incoming.h
|
||||
chat_helpers/bot_command.cpp
|
||||
chat_helpers/bot_command.h
|
||||
chat_helpers/bot_keyboard.cpp
|
||||
chat_helpers/bot_keyboard.h
|
||||
chat_helpers/emoji_keywords.cpp
|
||||
@@ -676,8 +685,6 @@ PRIVATE
|
||||
inline_bots/inline_bot_send_data.h
|
||||
inline_bots/inline_results_inner.cpp
|
||||
inline_bots/inline_results_inner.h
|
||||
inline_bots/inline_results_mosaic_layout.cpp
|
||||
inline_bots/inline_results_mosaic_layout.h
|
||||
inline_bots/inline_results_widget.cpp
|
||||
inline_bots/inline_results_widget.h
|
||||
intro/intro_code.cpp
|
||||
@@ -704,8 +711,10 @@ PRIVATE
|
||||
lang/lang_numbers_animation.h
|
||||
lang/lang_translator.cpp
|
||||
lang/lang_translator.h
|
||||
layout/layout_utils.cpp
|
||||
layout/layout_utils.h
|
||||
layout/layout_document_generic_preview.cpp
|
||||
layout/layout_document_generic_preview.h
|
||||
layout/layout_item_base.cpp
|
||||
layout/layout_item_base.h
|
||||
main/main_account.cpp
|
||||
main/main_account.h
|
||||
main/main_app_config.cpp
|
||||
@@ -819,8 +828,6 @@ PRIVATE
|
||||
overview/overview_layout.cpp
|
||||
overview/overview_layout.h
|
||||
overview/overview_layout_delegate.h
|
||||
overview/overview_mosaic_layout.cpp
|
||||
overview/overview_mosaic_layout.h
|
||||
passport/passport_encryption.cpp
|
||||
passport/passport_encryption.h
|
||||
passport/passport_form_controller.cpp
|
||||
@@ -873,7 +880,6 @@ PRIVATE
|
||||
platform/linux/notifications_manager_linux.h
|
||||
platform/linux/specific_linux.cpp
|
||||
platform/linux/specific_linux.h
|
||||
platform/linux/window_title_linux.h
|
||||
platform/mac/file_utilities_mac.mm
|
||||
platform/mac/file_utilities_mac.h
|
||||
platform/mac/launcher_mac.mm
|
||||
@@ -888,7 +894,6 @@ PRIVATE
|
||||
platform/mac/specific_mac_p.mm
|
||||
platform/mac/specific_mac_p.h
|
||||
platform/mac/window_title_mac.mm
|
||||
platform/mac/window_title_mac.h
|
||||
platform/mac/touchbar/items/mac_formatter_item.h
|
||||
platform/mac/touchbar/items/mac_formatter_item.mm
|
||||
platform/mac/touchbar/items/mac_pinned_chats_item.h
|
||||
@@ -919,8 +924,6 @@ PRIVATE
|
||||
platform/win/notifications_manager_win.h
|
||||
platform/win/specific_win.cpp
|
||||
platform/win/specific_win.h
|
||||
platform/win/window_title_win.cpp
|
||||
platform/win/window_title_win.h
|
||||
platform/win/windows_app_user_model_id.cpp
|
||||
platform/win/windows_app_user_model_id.h
|
||||
platform/win/windows_dlls.cpp
|
||||
@@ -1094,13 +1097,11 @@ PRIVATE
|
||||
window/window_outdated_bar.h
|
||||
window/window_peer_menu.cpp
|
||||
window/window_peer_menu.h
|
||||
window/window_section_common.h
|
||||
window/window_session_controller.cpp
|
||||
window/window_session_controller.h
|
||||
window/window_slide_animation.cpp
|
||||
window/window_slide_animation.h
|
||||
window/window_title_qt.cpp
|
||||
window/window_title_qt.h
|
||||
window/window_title.h
|
||||
window/window_top_bar_wrap.h
|
||||
window/themes/window_theme.cpp
|
||||
window/themes/window_theme.h
|
||||
@@ -1127,8 +1128,6 @@ PRIVATE
|
||||
config.h
|
||||
facades.cpp
|
||||
facades.h
|
||||
layout.cpp
|
||||
layout.h
|
||||
logs.cpp
|
||||
logs.h
|
||||
main.cpp
|
||||
@@ -1141,13 +1140,6 @@ PRIVATE
|
||||
stdafx.h
|
||||
)
|
||||
|
||||
if (NOT LINUX)
|
||||
remove_target_sources(Telegram ${src_loc}
|
||||
window/window_title_qt.cpp
|
||||
window/window_title_qt.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if (DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
|
||||
remove_target_sources(Telegram ${src_loc}
|
||||
platform/linux/linux_xdp_file_dialog.cpp
|
||||
|
||||
BIN
Telegram/Resources/icons/calls/video_mini_invited.png
Normal file
|
After Width: | Height: | Size: 359 B |
BIN
Telegram/Resources/icons/calls/video_mini_invited@2x.png
Normal file
|
After Width: | Height: | Size: 503 B |
BIN
Telegram/Resources/icons/calls/video_mini_invited@3x.png
Normal file
|
After Width: | Height: | Size: 878 B |
BIN
Telegram/Resources/icons/chat/input_autodelete_30d.png
Normal file
|
After Width: | Height: | Size: 1010 B |
BIN
Telegram/Resources/icons/chat/input_autodelete_30d@2x.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
Telegram/Resources/icons/chat/input_autodelete_30d@3x.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 666 B |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.0 KiB |
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
FullExecPath=$PWD
|
||||
pushd `dirname $0` > /dev/null
|
||||
FullScriptPath=`pwd`
|
||||
popd > /dev/null
|
||||
|
||||
if [ ! -d "$FullScriptPath/../../../../DesktopPrivate" ]; then
|
||||
echo ""
|
||||
echo "This script is for building the production version of Telegram Desktop."
|
||||
echo ""
|
||||
echo "For building custom versions please visit the build instructions page at:"
|
||||
echo "https://github.com/telegramdesktop/tdesktop/#build-instructions"
|
||||
exit
|
||||
fi
|
||||
|
||||
Error () {
|
||||
cd $FullExecPath
|
||||
echo "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cd $FullScriptPath/../../../../
|
||||
while IFS='' read -r line || [[ -n "$line" ]]; do
|
||||
tx pull -f -l $line --minimum-perc=100
|
||||
done < tdesktop/Telegram/Resources/langs/list
|
||||
cd translations/telegram-desktop.langstrings/
|
||||
for file in *.strings; do
|
||||
iconv -f "UTF-16LE" -t "UTF-8" "$file" > "../../tdesktop/Telegram/Resources/langs/lang_$file.tmp"
|
||||
awk '{ if (NR==1) sub(/^\xef\xbb\xbf/,""); sub(/
|
||||
/,""); print }' "../../tdesktop/Telegram/Resources/langs/lang_$file.tmp" > "../../tdesktop/Telegram/Resources/langs/lang_$file"
|
||||
rm "../../tdesktop/Telegram/Resources/langs/lang_$file.tmp"
|
||||
done
|
||||
|
||||
@@ -82,6 +82,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_box_ok" = "OK";
|
||||
"lng_box_done" = "Done";
|
||||
"lng_box_yes" = "Yes";
|
||||
"lng_box_no" = "No";
|
||||
|
||||
"lng_cancel" = "Cancel";
|
||||
"lng_continue" = "Continue";
|
||||
@@ -596,6 +598,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_cloud_password_remove" = "Remove cloud password";
|
||||
"lng_cloud_password_set" = "Enable two-step verification";
|
||||
"lng_cloud_password_edit" = "Change cloud password";
|
||||
"lng_cloud_password_reset_in" = "Reset password in";
|
||||
"lng_cloud_password_reset_ready" = "Reset password";
|
||||
"lng_cloud_password_reset_cancel" = "Cancel password reset";
|
||||
"lng_cloud_password_enter_old" = "Enter current password";
|
||||
"lng_cloud_password_enter_first" = "Enter a password";
|
||||
"lng_cloud_password_enter_new" = "Enter new password";
|
||||
@@ -618,6 +623,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_cloud_password_passport_losing" = "Warning! All data saved in your Telegram Passport will be lost!";
|
||||
"lng_cloud_password_resend" = "Resend code";
|
||||
"lng_cloud_password_resent" = "Code was resent.";
|
||||
"lng_cloud_password_reset_title" = "Reset password";
|
||||
"lng_cloud_password_reset_no_email" = "Since you didn't provide a recovery email when setting up your password, your remaining options are either to remember your password or wait 7 days until your password is reset.";
|
||||
"lng_cloud_password_reset_with_email" = "If you don't have access to your recovery email, your remaining options are either to remember your password or wait 7 days until your password resets.";
|
||||
"lng_cloud_password_reset_ok" = "Reset";
|
||||
"lng_cloud_password_reset_cancel_title" = "Cancel reset";
|
||||
"lng_cloud_password_reset_cancel_sure" = "Cancel the password reset process? If you request a new reset later, it will take another 7 days.";
|
||||
"lng_cloud_password_reset_later" = "You recently requested a password reset that was cancelled. Please wait {duration} before making a new request.";
|
||||
|
||||
"lng_connection_auto_connecting" = "Default (connecting...)";
|
||||
"lng_connection_auto" = "Default ({transport} used)";
|
||||
@@ -1005,8 +1017,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_manage_messages_ttl_title" = "Auto-delete messages";
|
||||
"lng_manage_messages_ttl_never" = "Off";
|
||||
"lng_manage_messages_ttl_after1" = "24 hours";
|
||||
"lng_manage_messages_ttl_after2" = "7 days";
|
||||
"lng_manage_messages_ttl_after1" = "1 day";
|
||||
"lng_manage_messages_ttl_after2" = "1 week";
|
||||
"lng_manage_messages_ttl_after3" = "1 month";
|
||||
|
||||
"lng_ttl_edit_about" = "Automatically delete new messages after a certain period of time for you and {user}.";
|
||||
"lng_ttl_edit_about_group" = "Automatically delete new messages sent in this chat after a certain period of time.";
|
||||
@@ -1015,8 +1028,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_ttl_about_tooltip" = "New messages in this chat will be automatically deleted in {duration}.";
|
||||
"lng_ttl_about_tooltip_channel" = "New messages in this chat will be automatically deleted in {duration}.";
|
||||
"lng_ttl_about_tooltip_off" = "Auto-delete is now disabled.";
|
||||
"lng_ttl_about_duration1" = "24 hours";
|
||||
"lng_ttl_about_duration2" = "7 days";
|
||||
"lng_ttl_about_duration1" = "1 day";
|
||||
"lng_ttl_about_duration2" = "1 week";
|
||||
"lng_ttl_about_duration3" = "1 month";
|
||||
|
||||
"lng_report_title" = "Report channel";
|
||||
"lng_report_group_title" = "Report group";
|
||||
@@ -2009,6 +2023,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_call_start_video" = "Start Video";
|
||||
"lng_call_stop_video" = "Stop Video";
|
||||
"lng_call_screencast" = "Screencast";
|
||||
"lng_call_end_call" = "End Call";
|
||||
"lng_call_mute_audio" = "Mute";
|
||||
"lng_call_unmute_audio" = "Unmute";
|
||||
@@ -2088,6 +2103,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_noise_suppression" = "Enable Noise Suppression";
|
||||
"lng_group_call_limit#one" = "Video is only available\nfor the first {count} member";
|
||||
"lng_group_call_limit#other" = "Video is only available\nfor the first {count} members";
|
||||
"lng_group_call_over_limit#one" = "The voice chat is over {count} member.\nNew participants only have access to audio stream.";
|
||||
"lng_group_call_over_limit#other" = "The voice chat is over {count} members.\nNew participants only have access to audio stream.";
|
||||
"lng_group_call_video_paused" = "Video is paused";
|
||||
"lng_group_call_share_speaker" = "Users with this link can speak";
|
||||
"lng_group_call_copy_speaker_link" = "Copy Speaker Link";
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
de,es,it,ko,nl,pt_BR
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
FullExecPath=$PWD
|
||||
pushd `dirname $0` > /dev/null
|
||||
FullScriptPath=`pwd`
|
||||
popd > /dev/null
|
||||
|
||||
if [ ! -d "$FullScriptPath/../../../../DesktopPrivate" ]; then
|
||||
echo ""
|
||||
echo "This script is for building the production version of Telegram Desktop."
|
||||
echo ""
|
||||
echo "For building custom versions please visit the build instructions page at:"
|
||||
echo "https://github.com/telegramdesktop/tdesktop/#build-instructions"
|
||||
exit
|
||||
fi
|
||||
|
||||
Error () {
|
||||
cd $FullExecPath
|
||||
echo "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
cd $FullScriptPath/../../../../
|
||||
while IFS='' read -r line || [[ -n "$line" ]]; do
|
||||
tx pull -f -l $line
|
||||
done < tdesktop/Telegram/Resources/langs/list
|
||||
tx push -s
|
||||
|
||||
cd $FullExecPath
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="2.8.12.0" />
|
||||
Version="2.9.3.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,8,12,0
|
||||
PRODUCTVERSION 2,8,12,0
|
||||
FILEVERSION 2,9,3,0
|
||||
PRODUCTVERSION 2,9,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "2.8.12.0"
|
||||
VALUE "FileVersion", "2.9.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.8.12.0"
|
||||
VALUE "ProductVersion", "2.9.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,8,12,0
|
||||
PRODUCTVERSION 2,8,12,0
|
||||
FILEVERSION 2,9,3,0
|
||||
PRODUCTVERSION 2,9,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "2.8.12.0"
|
||||
VALUE "FileVersion", "2.9.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.8.12.0"
|
||||
VALUE "ProductVersion", "2.9.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
173
Telegram/SourceFiles/api/api_blocked_peers.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
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_blocked_peers.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_peer_id.h"
|
||||
#include "data/data_session.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
constexpr auto kBlockedFirstSlice = 16;
|
||||
constexpr auto kBlockedPerPage = 40;
|
||||
|
||||
BlockedPeers::Slice TLToSlice(
|
||||
const MTPcontacts_Blocked &blocked,
|
||||
Data::Session &owner) {
|
||||
const auto create = [&](int count, const QVector<MTPPeerBlocked> &list) {
|
||||
auto slice = BlockedPeers::Slice();
|
||||
slice.total = std::max(count, list.size());
|
||||
slice.list.reserve(list.size());
|
||||
for (const auto &contact : list) {
|
||||
contact.match([&](const MTPDpeerBlocked &data) {
|
||||
slice.list.push_back({
|
||||
.id = peerFromMTP(data.vpeer_id()),
|
||||
.date = data.vdate().v,
|
||||
});
|
||||
});
|
||||
}
|
||||
return slice;
|
||||
};
|
||||
return blocked.match([&](const MTPDcontacts_blockedSlice &data) {
|
||||
owner.processUsers(data.vusers());
|
||||
owner.processChats(data.vchats());
|
||||
return create(data.vcount().v, data.vblocked().v);
|
||||
}, [&](const MTPDcontacts_blocked &data) {
|
||||
owner.processUsers(data.vusers());
|
||||
owner.processChats(data.vchats());
|
||||
return create(0, data.vblocked().v);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BlockedPeers::BlockedPeers(not_null<ApiWrap*> api)
|
||||
: _session(&api->session())
|
||||
, _api(&api->instance()) {
|
||||
}
|
||||
|
||||
bool BlockedPeers::Slice::Item::operator==(const Item &other) const {
|
||||
return (id == other.id) && (date == other.date);
|
||||
}
|
||||
|
||||
bool BlockedPeers::Slice::Item::operator!=(const Item &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool BlockedPeers::Slice::operator==(const BlockedPeers::Slice &other) const {
|
||||
return (total == other.total) && (list == other.list);
|
||||
}
|
||||
|
||||
bool BlockedPeers::Slice::operator!=(const BlockedPeers::Slice &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void BlockedPeers::block(not_null<PeerData*> peer) {
|
||||
if (peer->isBlocked()) {
|
||||
_session->changes().peerUpdated(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::IsBlocked);
|
||||
} else if (_blockRequests.find(peer) == end(_blockRequests)) {
|
||||
const auto requestId = _api.request(MTPcontacts_Block(
|
||||
peer->input
|
||||
)).done([=](const MTPBool &result) {
|
||||
_blockRequests.erase(peer);
|
||||
peer->setIsBlocked(true);
|
||||
if (_slice) {
|
||||
_slice->list.insert(
|
||||
_slice->list.begin(),
|
||||
{ peer->id, base::unixtime::now() });
|
||||
++_slice->total;
|
||||
_changes.fire_copy(*_slice);
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_blockRequests.erase(peer);
|
||||
}).send();
|
||||
|
||||
_blockRequests.emplace(peer, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockedPeers::unblock(not_null<PeerData*> peer, Fn<void()> onDone) {
|
||||
if (!peer->isBlocked()) {
|
||||
_session->changes().peerUpdated(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::IsBlocked);
|
||||
return;
|
||||
} else if (_blockRequests.find(peer) != end(_blockRequests)) {
|
||||
return;
|
||||
}
|
||||
const auto requestId = _api.request(MTPcontacts_Unblock(
|
||||
peer->input
|
||||
)).done([=](const MTPBool &result) {
|
||||
_blockRequests.erase(peer);
|
||||
peer->setIsBlocked(false);
|
||||
if (_slice) {
|
||||
auto &list = _slice->list;
|
||||
for (auto i = list.begin(); i != list.end(); ++i) {
|
||||
if (i->id == peer->id) {
|
||||
list.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_slice->total > list.size()) {
|
||||
--_slice->total;
|
||||
}
|
||||
_changes.fire_copy(*_slice);
|
||||
}
|
||||
if (onDone) {
|
||||
onDone();
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_blockRequests.erase(peer);
|
||||
}).send();
|
||||
_blockRequests.emplace(peer, requestId);
|
||||
}
|
||||
|
||||
void BlockedPeers::reload() {
|
||||
if (_requestId) {
|
||||
return;
|
||||
}
|
||||
request(0, [=](Slice &&slice) {
|
||||
if (!_slice || *_slice != slice) {
|
||||
_slice = slice;
|
||||
_changes.fire(std::move(slice));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto BlockedPeers::slice() -> rpl::producer<BlockedPeers::Slice> {
|
||||
if (!_slice) {
|
||||
reload();
|
||||
}
|
||||
return _slice
|
||||
? _changes.events_starting_with_copy(*_slice)
|
||||
: (_changes.events() | rpl::type_erased());
|
||||
}
|
||||
|
||||
void BlockedPeers::request(int offset, Fn<void(BlockedPeers::Slice)> onDone) {
|
||||
if (_requestId) {
|
||||
return;
|
||||
}
|
||||
_requestId = _api.request(MTPcontacts_GetBlocked(
|
||||
MTP_int(offset),
|
||||
MTP_int(offset ? kBlockedPerPage : kBlockedFirstSlice)
|
||||
)).done([=](const MTPcontacts_Blocked &result) {
|
||||
_requestId = 0;
|
||||
onDone(TLToSlice(result, _session->data()));
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_requestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
60
Telegram/SourceFiles/api/api_blocked_peers.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Api {
|
||||
|
||||
class BlockedPeers final {
|
||||
public:
|
||||
struct Slice {
|
||||
struct Item {
|
||||
PeerId id;
|
||||
TimeId date = 0;
|
||||
|
||||
bool operator==(const Item &other) const;
|
||||
bool operator!=(const Item &other) const;
|
||||
};
|
||||
|
||||
QVector<Item> list;
|
||||
int total = 0;
|
||||
|
||||
bool operator==(const Slice &other) const;
|
||||
bool operator!=(const Slice &other) const;
|
||||
};
|
||||
|
||||
explicit BlockedPeers(not_null<ApiWrap*> api);
|
||||
|
||||
void reload();
|
||||
rpl::producer<Slice> slice();
|
||||
void request(int offset, Fn<void(Slice)> onDone);
|
||||
|
||||
void block(not_null<PeerData*> peer);
|
||||
void unblock(not_null<PeerData*> peer, Fn<void()> onDone = nullptr);
|
||||
|
||||
private:
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
MTP::Sender _api;
|
||||
|
||||
base::flat_map<not_null<PeerData*>, mtpRequestId> _blockRequests;
|
||||
mtpRequestId _requestId = 0;
|
||||
std::optional<Slice> _slice;
|
||||
rpl::event_stream<Slice> _changes;
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace Api
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_bot.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_cloud_password.h"
|
||||
#include "core/core_cloud_password.h"
|
||||
#include "api/api_send_progress.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
@@ -168,7 +169,7 @@ void SendBotCallbackDataWithPassword(
|
||||
if (!button || button->requestId) {
|
||||
return;
|
||||
}
|
||||
api->reloadPasswordState();
|
||||
api->cloudPassword().reload();
|
||||
SendBotCallbackData(item, row, column, MTP_inputCheckPasswordEmpty(), [=](const MTP::Error &error) {
|
||||
auto box = PrePasswordErrorBox(
|
||||
error,
|
||||
@@ -181,7 +182,7 @@ void SendBotCallbackDataWithPassword(
|
||||
} else {
|
||||
auto lifetime = std::make_shared<rpl::lifetime>();
|
||||
button->requestId = -1;
|
||||
api->passwordState(
|
||||
api->cloudPassword().state(
|
||||
) | rpl::take(
|
||||
1
|
||||
) | rpl::start_with_next([=](const Core::CloudPasswordState &state) mutable {
|
||||
|
||||
86
Telegram/SourceFiles/api/api_cloud_password.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
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_cloud_password.h"
|
||||
|
||||
#include "base/openssl_help.h"
|
||||
#include "core/core_cloud_password.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
namespace Api {
|
||||
|
||||
// #TODO Add ability to set recovery email separately.
|
||||
|
||||
CloudPassword::CloudPassword(not_null<ApiWrap*> api)
|
||||
: _api(&api->instance()) {
|
||||
}
|
||||
|
||||
void CloudPassword::reload() {
|
||||
if (_requestId) {
|
||||
return;
|
||||
}
|
||||
_requestId = _api.request(MTPaccount_GetPassword(
|
||||
)).done([=](const MTPaccount_Password &result) {
|
||||
_requestId = 0;
|
||||
result.match([&](const MTPDaccount_password &data) {
|
||||
openssl::AddRandomSeed(bytes::make_span(data.vsecure_random().v));
|
||||
if (_state) {
|
||||
*_state = Core::ParseCloudPasswordState(data);
|
||||
} else {
|
||||
_state = std::make_unique<Core::CloudPasswordState>(
|
||||
Core::ParseCloudPasswordState(data));
|
||||
}
|
||||
_stateChanges.fire_copy(*_state);
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_requestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
void CloudPassword::applyPendingReset(
|
||||
const MTPaccount_ResetPasswordResult &data) {
|
||||
if (!_state) {
|
||||
reload();
|
||||
return;
|
||||
}
|
||||
data.match([&](const MTPDaccount_resetPasswordOk &data) {
|
||||
reload();
|
||||
}, [&](const MTPDaccount_resetPasswordRequestedWait &data) {
|
||||
const auto until = data.vuntil_date().v;
|
||||
if (_state->pendingResetDate != until) {
|
||||
_state->pendingResetDate = until;
|
||||
_stateChanges.fire_copy(*_state);
|
||||
}
|
||||
}, [&](const MTPDaccount_resetPasswordFailedWait &data) {
|
||||
});
|
||||
}
|
||||
|
||||
void CloudPassword::clearUnconfirmedPassword() {
|
||||
_requestId = _api.request(MTPaccount_CancelPasswordEmail(
|
||||
)).done([=](const MTPBool &result) {
|
||||
_requestId = 0;
|
||||
reload();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_requestId = 0;
|
||||
reload();
|
||||
}).send();
|
||||
}
|
||||
|
||||
rpl::producer<Core::CloudPasswordState> CloudPassword::state() const {
|
||||
return _state
|
||||
? _stateChanges.events_starting_with_copy(*_state)
|
||||
: (_stateChanges.events() | rpl::type_erased());
|
||||
}
|
||||
|
||||
auto CloudPassword::stateCurrent() const
|
||||
-> std::optional<Core::CloudPasswordState> {
|
||||
return _state
|
||||
? base::make_optional(*_state)
|
||||
: std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
42
Telegram/SourceFiles/api/api_cloud_password.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
namespace Core {
|
||||
struct CloudPasswordState;
|
||||
} // namespace Core
|
||||
|
||||
class ApiWrap;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Api {
|
||||
|
||||
class CloudPassword final {
|
||||
public:
|
||||
explicit CloudPassword(not_null<ApiWrap*> api);
|
||||
|
||||
void reload();
|
||||
void applyPendingReset(const MTPaccount_ResetPasswordResult &data);
|
||||
void clearUnconfirmedPassword();
|
||||
rpl::producer<Core::CloudPasswordState> state() const;
|
||||
std::optional<Core::CloudPasswordState> stateCurrent() const;
|
||||
|
||||
private:
|
||||
MTP::Sender _api;
|
||||
mtpRequestId _requestId = 0;
|
||||
std::unique_ptr<Core::CloudPasswordState> _state;
|
||||
rpl::event_stream<Core::CloudPasswordState> _stateChanges;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Api
|
||||
@@ -40,22 +40,22 @@ namespace {
|
||||
void InnerFillMessagePostFlags(
|
||||
const Api::SendOptions &options,
|
||||
not_null<PeerData*> peer,
|
||||
MTPDmessage::Flags &flags) {
|
||||
MessageFlags &flags) {
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
if (!anonymousPost) {
|
||||
flags |= MTPDmessage::Flag::f_from_id;
|
||||
flags |= MessageFlag::HasFromId;
|
||||
return;
|
||||
} else if (peer->asMegagroup()) {
|
||||
return;
|
||||
}
|
||||
flags |= MTPDmessage::Flag::f_post;
|
||||
flags |= MessageFlag::Post;
|
||||
// Don't display views and author of a new post when it's scheduled.
|
||||
if (options.scheduled) {
|
||||
return;
|
||||
}
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
flags |= MessageFlag::HasViews;
|
||||
if (peer->asChannel()->addsSignature()) {
|
||||
flags |= MTPDmessage::Flag::f_post_author;
|
||||
flags |= MessageFlag::HasPostAuthor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,11 +79,10 @@ void SendExistingMedia(
|
||||
session->data().nextLocalMessageId());
|
||||
const auto randomId = openssl::RandomValue<uint64>();
|
||||
|
||||
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto flags = NewMessageFlags(peer);
|
||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||
if (message.action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to;
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
@@ -111,19 +110,19 @@ void SendExistingMedia(
|
||||
const auto captionText = caption.text;
|
||||
|
||||
if (message.action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
flags |= MessageFlag::LocalHistoryEntry;
|
||||
}
|
||||
|
||||
session->data().registerMessageRandomId(randomId, newId);
|
||||
|
||||
const auto viaBotId = UserId();
|
||||
history->addNewLocalMessage(
|
||||
newId.msg,
|
||||
flags,
|
||||
clientFlags,
|
||||
0,
|
||||
viaBotId,
|
||||
replyTo,
|
||||
HistoryItem::NewMessageDate(message.action.options.scheduled),
|
||||
messageFromId,
|
||||
@@ -253,11 +252,10 @@ bool SendDice(Api::MessageToSend &message) {
|
||||
const auto randomId = openssl::RandomValue<uint64>();
|
||||
|
||||
auto &histories = history->owner().histories();
|
||||
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto flags = NewMessageFlags(peer);
|
||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||
if (message.action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to;
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
const auto replyHeader = NewMessageReplyHeader(message.action);
|
||||
@@ -272,42 +270,26 @@ bool SendDice(Api::MessageToSend &message) {
|
||||
const auto replyTo = message.action.replyTo;
|
||||
|
||||
if (message.action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
flags |= MessageFlag::LocalHistoryEntry;
|
||||
}
|
||||
|
||||
session->data().registerMessageRandomId(randomId, newId);
|
||||
|
||||
const auto views = 1;
|
||||
const auto forwards = 0;
|
||||
history->addNewMessage(
|
||||
MTP_message(
|
||||
MTP_flags(flags),
|
||||
MTP_int(newId.msg),
|
||||
peerToMTP(messageFromId),
|
||||
peerToMTP(history->peer->id),
|
||||
MTPMessageFwdHeader(),
|
||||
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(views),
|
||||
MTP_int(forwards),
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTP_string(messagePostAuthor),
|
||||
MTPlong(),
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>(),
|
||||
MTPint()), // ttl_period
|
||||
clientFlags,
|
||||
NewMessageType::Unread);
|
||||
const auto viaBotId = UserId();
|
||||
history->addNewLocalMessage(
|
||||
newId.msg,
|
||||
flags,
|
||||
viaBotId,
|
||||
message.action.replyTo,
|
||||
HistoryItem::NewMessageDate(message.action.options.scheduled),
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
TextWithEntities(),
|
||||
MTP_messageMediaDice(MTP_int(0), MTP_string(emoji)),
|
||||
MTPReplyMarkup());
|
||||
|
||||
const auto requestType = Data::Histories::RequestType::Send;
|
||||
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||
@@ -338,14 +320,15 @@ bool SendDice(Api::MessageToSend &message) {
|
||||
void FillMessagePostFlags(
|
||||
const Api::SendAction &action,
|
||||
not_null<PeerData*> peer,
|
||||
MTPDmessage::Flags &flags) {
|
||||
MessageFlags &flags) {
|
||||
InnerFillMessagePostFlags(action.options, peer, flags);
|
||||
}
|
||||
|
||||
void SendConfirmedFile(
|
||||
not_null<Main::Session*> session,
|
||||
const std::shared_ptr<FileLoadResult> &file) {
|
||||
const auto isEditing = file->to.replaceMediaOf != 0;
|
||||
const auto isEditing = (file->type != SendMediaType::Audio)
|
||||
&& (file->to.replaceMediaOf != 0);
|
||||
const auto channelId = peerToChannel(file->to.peer);
|
||||
|
||||
const auto newId = FullMsgId(
|
||||
@@ -396,29 +379,24 @@ void SendConfirmedFile(
|
||||
}
|
||||
}
|
||||
|
||||
auto flags = (isEditing ? MTPDmessage::Flags() : NewMessageFlags(peer))
|
||||
| MTPDmessage::Flag::f_entities
|
||||
| MTPDmessage::Flag::f_media;
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto flags = isEditing ? MessageFlags() : NewMessageFlags(peer);
|
||||
if (file->to.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to;
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
}
|
||||
const auto replyHeader = NewMessageReplyHeader(action);
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = ShouldSendSilent(peer, file->to.options);
|
||||
Api::FillMessagePostFlags(action, peer, flags);
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (silentPost) {
|
||||
flags |= MTPDmessage::Flag::f_silent;
|
||||
}
|
||||
if (groupId) {
|
||||
flags |= MTPDmessage::Flag::f_grouped_id;
|
||||
flags |= MessageFlag::Silent;
|
||||
}
|
||||
if (file->to.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
|
||||
// Scheduled messages have no the 'edited' badge.
|
||||
flags |= MTPDmessage::Flag::f_edit_hide;
|
||||
flags |= MessageFlag::HideEdited;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
flags |= MessageFlag::LocalHistoryEntry;
|
||||
}
|
||||
|
||||
const auto messageFromId = anonymousPost ? 0 : session->userPeerId();
|
||||
@@ -426,17 +404,37 @@ void SendConfirmedFile(
|
||||
? 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(
|
||||
MTP_flags(photoFlags),
|
||||
file->photo,
|
||||
MTPint());
|
||||
const auto media = [&] {
|
||||
if (file->type == SendMediaType::Photo) {
|
||||
return MTP_messageMediaPhoto(
|
||||
MTP_flags(MTPDmessageMediaPhoto::Flag::f_photo),
|
||||
file->photo,
|
||||
MTPint());
|
||||
} else if (file->type == SendMediaType::File) {
|
||||
return MTP_messageMediaDocument(
|
||||
MTP_flags(MTPDmessageMediaDocument::Flag::f_document),
|
||||
file->document,
|
||||
MTPint());
|
||||
} else if (file->type == SendMediaType::Audio) {
|
||||
return MTP_messageMediaDocument(
|
||||
MTP_flags(MTPDmessageMediaDocument::Flag::f_document),
|
||||
file->document,
|
||||
MTPint());
|
||||
} else {
|
||||
Unexpected("Type in sendFilesConfirmed.");
|
||||
}
|
||||
}();
|
||||
|
||||
const auto mtpMessage = MTP_message(
|
||||
MTP_flags(flags),
|
||||
if (itemToEdit) {
|
||||
itemToEdit->savePreviousMedia();
|
||||
itemToEdit->applyEdition(MTP_message(
|
||||
MTP_flags(MTPDmessage::Flag::f_media
|
||||
| ((flags & MessageFlag::HideEdited)
|
||||
? MTPDmessage::Flag::f_edit_hide
|
||||
: MTPDmessage::Flag())
|
||||
| (localEntities.v.isEmpty()
|
||||
? MTPDmessage::Flag()
|
||||
: MTPDmessage::Flag::f_entities)),
|
||||
MTP_int(newId.msg),
|
||||
peerToMTP(messageFromId),
|
||||
peerToMTP(file->to.peer),
|
||||
@@ -445,105 +443,32 @@ void SendConfirmedFile(
|
||||
replyHeader,
|
||||
MTP_int(HistoryItem::NewMessageDate(file->to.options.scheduled)),
|
||||
MTP_string(caption.text),
|
||||
photo,
|
||||
media,
|
||||
MTPReplyMarkup(),
|
||||
localEntities,
|
||||
MTP_int(views),
|
||||
MTP_int(forwards),
|
||||
MTPint(), // views
|
||||
MTPint(), // forwards
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTP_string(messagePostAuthor),
|
||||
MTP_long(groupId),
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>(),
|
||||
MTPint()); // ttl_period
|
||||
|
||||
if (itemToEdit) {
|
||||
itemToEdit->savePreviousMedia();
|
||||
itemToEdit->applyEdition(mtpMessage.c_message());
|
||||
} else {
|
||||
history->addNewMessage(
|
||||
mtpMessage,
|
||||
clientFlags,
|
||||
NewMessageType::Unread);
|
||||
}
|
||||
} else if (file->type == SendMediaType::File) {
|
||||
const auto documentFlags = MTPDmessageMediaDocument::Flag::f_document | 0;
|
||||
const auto document = MTP_messageMediaDocument(
|
||||
MTP_flags(documentFlags),
|
||||
file->document,
|
||||
MTPint());
|
||||
|
||||
const auto mtpMessage = MTP_message(
|
||||
MTP_flags(flags),
|
||||
MTP_int(newId.msg),
|
||||
peerToMTP(messageFromId),
|
||||
peerToMTP(file->to.peer),
|
||||
MTPMessageFwdHeader(),
|
||||
MTPint(),
|
||||
replyHeader,
|
||||
MTP_int(HistoryItem::NewMessageDate(file->to.options.scheduled)),
|
||||
MTP_string(caption.text),
|
||||
document,
|
||||
MTPReplyMarkup(),
|
||||
localEntities,
|
||||
MTP_int(views),
|
||||
MTP_int(forwards),
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTP_string(messagePostAuthor),
|
||||
MTP_long(groupId),
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>(),
|
||||
MTPint()); // ttl_period
|
||||
|
||||
if (itemToEdit) {
|
||||
itemToEdit->savePreviousMedia();
|
||||
itemToEdit->applyEdition(mtpMessage.c_message());
|
||||
} else {
|
||||
history->addNewMessage(
|
||||
mtpMessage,
|
||||
clientFlags,
|
||||
NewMessageType::Unread);
|
||||
}
|
||||
} else if (file->type == SendMediaType::Audio) {
|
||||
if (!peer->isChannel() || peer->isMegagroup()) {
|
||||
flags |= MTPDmessage::Flag::f_media_unread;
|
||||
}
|
||||
const auto documentFlags = MTPDmessageMediaDocument::Flag::f_document | 0;
|
||||
const auto document = MTP_messageMediaDocument(
|
||||
MTP_flags(documentFlags),
|
||||
file->document,
|
||||
MTPint());
|
||||
history->addNewMessage(
|
||||
MTP_message(
|
||||
MTP_flags(flags),
|
||||
MTP_int(newId.msg),
|
||||
peerToMTP(messageFromId),
|
||||
peerToMTP(file->to.peer),
|
||||
MTPMessageFwdHeader(),
|
||||
MTPint(),
|
||||
replyHeader,
|
||||
MTP_int(
|
||||
HistoryItem::NewMessageDate(file->to.options.scheduled)),
|
||||
MTP_string(caption.text),
|
||||
document,
|
||||
MTPReplyMarkup(),
|
||||
localEntities,
|
||||
MTP_int(views),
|
||||
MTP_int(forwards),
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTP_string(messagePostAuthor),
|
||||
MTP_long(groupId),
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>(),
|
||||
MTPint()), // ttl_period
|
||||
clientFlags,
|
||||
NewMessageType::Unread);
|
||||
// Voices can't be edited.
|
||||
MTPint()).c_message());
|
||||
} else {
|
||||
Unexpected("Type in sendFilesConfirmed.");
|
||||
const auto viaBotId = UserId();
|
||||
history->addNewLocalMessage(
|
||||
newId.msg,
|
||||
flags,
|
||||
viaBotId,
|
||||
file->to.replyTo,
|
||||
HistoryItem::NewMessageDate(file->to.options.scheduled),
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
caption,
|
||||
media,
|
||||
MTPReplyMarkup(),
|
||||
groupId);
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
|
||||
@@ -30,7 +30,7 @@ bool SendDice(Api::MessageToSend &message);
|
||||
void FillMessagePostFlags(
|
||||
const SendAction &action,
|
||||
not_null<PeerData*> peer,
|
||||
MTPDmessage::Flags &flags);
|
||||
MessageFlags &flags);
|
||||
|
||||
void SendConfirmedFile(
|
||||
not_null<Main::Session*> session,
|
||||
|
||||
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "api/api_authorizations.h"
|
||||
#include "api/api_text_entities.h"
|
||||
#include "api/api_user_privacy.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_account.h"
|
||||
#include "mtproto/mtp_instance.h"
|
||||
@@ -41,7 +42,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_controller.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "apiwrap.h"
|
||||
#include "app.h" // App::formatPhone
|
||||
#include "ui/text/format_values.h" // Ui::FormatPhone
|
||||
#include "app.h" // App::quitting
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
@@ -1054,7 +1056,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>(),
|
||||
MTP_int(d.vttl_period().value_or_empty())),
|
||||
MTPDmessage_ClientFlags(),
|
||||
MessageFlags(),
|
||||
NewMessageType::Unread);
|
||||
} break;
|
||||
|
||||
@@ -1085,7 +1087,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>(),
|
||||
MTP_int(d.vttl_period().value_or_empty())),
|
||||
MTPDmessage_ClientFlags(),
|
||||
MessageFlags(),
|
||||
NewMessageType::Unread);
|
||||
} break;
|
||||
|
||||
@@ -1114,7 +1116,7 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||
if (needToAdd) {
|
||||
_session->data().addNewMessage(
|
||||
d.vmessage(),
|
||||
MTPDmessage_ClientFlags(),
|
||||
MessageFlags(),
|
||||
NewMessageType::Unread);
|
||||
}
|
||||
} break;
|
||||
@@ -1208,7 +1210,7 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||
if (needToAdd) {
|
||||
_session->data().addNewMessage(
|
||||
d.vmessage(),
|
||||
MTPDmessage_ClientFlags(),
|
||||
MessageFlags(),
|
||||
NewMessageType::Unread);
|
||||
}
|
||||
} break;
|
||||
@@ -1844,7 +1846,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
|| user->isSelf()
|
||||
|| user->phone().isEmpty())
|
||||
? QString()
|
||||
: App::formatPhone(user->phone())),
|
||||
: Ui::FormatPhone(user->phone())),
|
||||
user->username);
|
||||
|
||||
session().changes().peerUpdated(
|
||||
@@ -1953,13 +1955,10 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (const auto key = ApiWrap::Privacy::KeyFromMTP(d.vkey().type())) {
|
||||
if (allLoaded()) {
|
||||
session().api().handlePrivacyChange(*key, d.vrules());
|
||||
} else {
|
||||
session().api().reloadPrivacy(*key);
|
||||
}
|
||||
}
|
||||
session().api().userPrivacy().apply(
|
||||
d.vkey().type(),
|
||||
d.vrules(),
|
||||
allLoaded());
|
||||
} break;
|
||||
|
||||
case mtpc_updatePinnedDialogs: {
|
||||
|
||||
303
Telegram/SourceFiles/api/api_user_privacy.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
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_user_privacy.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_peer_id.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxRules = 3; // Allow users, disallow users, Option.
|
||||
|
||||
using TLInputRules = MTPVector<MTPInputPrivacyRule>;
|
||||
using TLRules = MTPVector<MTPPrivacyRule>;
|
||||
|
||||
TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
|
||||
const auto collectInputUsers = [](const auto &peers) {
|
||||
auto result = QVector<MTPInputUser>();
|
||||
result.reserve(peers.size());
|
||||
for (const auto peer : peers) {
|
||||
if (const auto user = peer->asUser()) {
|
||||
result.push_back(user->inputUser);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const auto collectInputChats = [](const auto &peers) {
|
||||
auto result = QVector<MTPint>(); // #TODO ids
|
||||
result.reserve(peers.size());
|
||||
for (const auto peer : peers) {
|
||||
if (!peer->isUser()) {
|
||||
result.push_back(peerToBareMTPInt(peer->id));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
auto result = QVector<MTPInputPrivacyRule>();
|
||||
result.reserve(kMaxRules);
|
||||
if (!rule.ignoreAlways) {
|
||||
const auto users = collectInputUsers(rule.always);
|
||||
const auto chats = collectInputChats(rule.always);
|
||||
if (!users.empty()) {
|
||||
result.push_back(
|
||||
MTP_inputPrivacyValueAllowUsers(
|
||||
MTP_vector<MTPInputUser>(users)));
|
||||
}
|
||||
if (!chats.empty()) {
|
||||
result.push_back(
|
||||
MTP_inputPrivacyValueAllowChatParticipants(
|
||||
MTP_vector<MTPint>(chats)));
|
||||
}
|
||||
}
|
||||
if (!rule.ignoreNever) {
|
||||
const auto users = collectInputUsers(rule.never);
|
||||
const auto chats = collectInputChats(rule.never);
|
||||
if (!users.empty()) {
|
||||
result.push_back(
|
||||
MTP_inputPrivacyValueDisallowUsers(
|
||||
MTP_vector<MTPInputUser>(users)));
|
||||
}
|
||||
if (!chats.empty()) {
|
||||
result.push_back(
|
||||
MTP_inputPrivacyValueDisallowChatParticipants(
|
||||
MTP_vector<MTPint>(chats)));
|
||||
}
|
||||
}
|
||||
result.push_back([&] {
|
||||
using Option = UserPrivacy::Option;
|
||||
switch (rule.option) {
|
||||
case Option::Everyone: return MTP_inputPrivacyValueAllowAll();
|
||||
case Option::Contacts: return MTP_inputPrivacyValueAllowContacts();
|
||||
case Option::Nobody: return MTP_inputPrivacyValueDisallowAll();
|
||||
}
|
||||
Unexpected("Option value in Api::UserPrivacy::RulesToTL.");
|
||||
}());
|
||||
|
||||
|
||||
return MTP_vector<MTPInputPrivacyRule>(std::move(result));
|
||||
}
|
||||
|
||||
UserPrivacy::Rule TLToRules(const TLRules &rules, Data::Session &owner) {
|
||||
// This is simplified version of privacy rules interpretation.
|
||||
// But it should be fine for all the apps
|
||||
// that use the same subset of features.
|
||||
using Option = UserPrivacy::Option;
|
||||
auto result = UserPrivacy::Rule();
|
||||
auto optionSet = false;
|
||||
const auto setOption = [&](Option option) {
|
||||
if (optionSet) return;
|
||||
optionSet = true;
|
||||
result.option = option;
|
||||
};
|
||||
auto &always = result.always;
|
||||
auto &never = result.never;
|
||||
const auto feed = [&](const MTPPrivacyRule &rule) {
|
||||
rule.match([&](const MTPDprivacyValueAllowAll &) {
|
||||
setOption(Option::Everyone);
|
||||
}, [&](const MTPDprivacyValueAllowContacts &) {
|
||||
setOption(Option::Contacts);
|
||||
}, [&](const MTPDprivacyValueAllowUsers &data) {
|
||||
const auto &users = data.vusers().v;
|
||||
always.reserve(always.size() + users.size());
|
||||
for (const auto userId : users) {
|
||||
const auto user = owner.user(UserId(userId.v));
|
||||
if (!base::contains(never, user)
|
||||
&& !base::contains(always, user)) {
|
||||
always.emplace_back(user);
|
||||
}
|
||||
}
|
||||
}, [&](const MTPDprivacyValueAllowChatParticipants &data) {
|
||||
const auto &chats = data.vchats().v;
|
||||
always.reserve(always.size() + chats.size());
|
||||
for (const auto &chatId : chats) {
|
||||
const auto chat = owner.chatLoaded(chatId);
|
||||
const auto peer = chat
|
||||
? static_cast<PeerData*>(chat)
|
||||
: owner.channelLoaded(chatId);
|
||||
if (peer
|
||||
&& !base::contains(never, peer)
|
||||
&& !base::contains(always, peer)) {
|
||||
always.emplace_back(peer);
|
||||
}
|
||||
}
|
||||
}, [&](const MTPDprivacyValueDisallowContacts &) {
|
||||
// Not supported
|
||||
}, [&](const MTPDprivacyValueDisallowAll &) {
|
||||
setOption(Option::Nobody);
|
||||
}, [&](const MTPDprivacyValueDisallowUsers &data) {
|
||||
const auto &users = data.vusers().v;
|
||||
never.reserve(never.size() + users.size());
|
||||
for (const auto userId : users) {
|
||||
const auto user = owner.user(UserId(userId.v));
|
||||
if (!base::contains(always, user)
|
||||
&& !base::contains(never, user)) {
|
||||
never.emplace_back(user);
|
||||
}
|
||||
}
|
||||
}, [&](const MTPDprivacyValueDisallowChatParticipants &data) {
|
||||
const auto &chats = data.vchats().v;
|
||||
never.reserve(never.size() + chats.size());
|
||||
for (const auto &chatId : chats) {
|
||||
const auto chat = owner.chatLoaded(chatId);
|
||||
const auto peer = chat
|
||||
? static_cast<PeerData*>(chat)
|
||||
: owner.channelLoaded(chatId);
|
||||
if (peer
|
||||
&& !base::contains(always, peer)
|
||||
&& !base::contains(never, peer)) {
|
||||
never.emplace_back(peer);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
for (const auto &rule : rules.v) {
|
||||
feed(rule);
|
||||
}
|
||||
feed(MTP_privacyValueDisallowAll()); // Disallow by default.
|
||||
return result;
|
||||
}
|
||||
|
||||
MTPInputPrivacyKey KeyToTL(UserPrivacy::Key key) {
|
||||
using Key = UserPrivacy::Key;
|
||||
switch (key) {
|
||||
case Key::Calls: return MTP_inputPrivacyKeyPhoneCall();
|
||||
case Key::Invites: return MTP_inputPrivacyKeyChatInvite();
|
||||
case Key::PhoneNumber: return MTP_inputPrivacyKeyPhoneNumber();
|
||||
case Key::AddedByPhone:
|
||||
return MTP_inputPrivacyKeyAddedByPhone();
|
||||
case Key::LastSeen:
|
||||
return MTP_inputPrivacyKeyStatusTimestamp();
|
||||
case Key::CallsPeer2Peer:
|
||||
return MTP_inputPrivacyKeyPhoneP2P();
|
||||
case Key::Forwards:
|
||||
return MTP_inputPrivacyKeyForwards();
|
||||
case Key::ProfilePhoto:
|
||||
return MTP_inputPrivacyKeyProfilePhoto();
|
||||
}
|
||||
Unexpected("Key in Api::UserPrivacy::KetToTL.");
|
||||
}
|
||||
|
||||
std::optional<UserPrivacy::Key> TLToKey(mtpTypeId type) {
|
||||
using Key = UserPrivacy::Key;
|
||||
switch (type) {
|
||||
case mtpc_privacyKeyPhoneNumber:
|
||||
case mtpc_inputPrivacyKeyPhoneNumber: return Key::PhoneNumber;
|
||||
case mtpc_privacyKeyAddedByPhone:
|
||||
case mtpc_inputPrivacyKeyAddedByPhone: return Key::AddedByPhone;
|
||||
case mtpc_privacyKeyStatusTimestamp:
|
||||
case mtpc_inputPrivacyKeyStatusTimestamp: return Key::LastSeen;
|
||||
case mtpc_privacyKeyChatInvite:
|
||||
case mtpc_inputPrivacyKeyChatInvite: return Key::Invites;
|
||||
case mtpc_privacyKeyPhoneCall:
|
||||
case mtpc_inputPrivacyKeyPhoneCall: return Key::Calls;
|
||||
case mtpc_privacyKeyPhoneP2P:
|
||||
case mtpc_inputPrivacyKeyPhoneP2P: return Key::CallsPeer2Peer;
|
||||
case mtpc_privacyKeyForwards:
|
||||
case mtpc_inputPrivacyKeyForwards: return Key::Forwards;
|
||||
case mtpc_privacyKeyProfilePhoto:
|
||||
case mtpc_inputPrivacyKeyProfilePhoto: return Key::ProfilePhoto;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UserPrivacy::UserPrivacy(not_null<ApiWrap*> api)
|
||||
: _session(&api->session())
|
||||
, _api(&api->instance()) {
|
||||
}
|
||||
|
||||
void UserPrivacy::save(
|
||||
Key key,
|
||||
const UserPrivacy::Rule &rule) {
|
||||
const auto tlKey = KeyToTL(key);
|
||||
const auto keyTypeId = tlKey.type();
|
||||
const auto it = _privacySaveRequests.find(keyTypeId);
|
||||
if (it != _privacySaveRequests.cend()) {
|
||||
_api.request(it->second).cancel();
|
||||
_privacySaveRequests.erase(it);
|
||||
}
|
||||
|
||||
const auto requestId = _api.request(MTPaccount_SetPrivacy(
|
||||
tlKey,
|
||||
RulesToTL(rule)
|
||||
)).done([=](const MTPaccount_PrivacyRules &result) {
|
||||
result.match([&](const MTPDaccount_privacyRules &data) {
|
||||
_session->data().processUsers(data.vusers());
|
||||
_session->data().processChats(data.vchats());
|
||||
_privacySaveRequests.remove(keyTypeId);
|
||||
apply(keyTypeId, data.vrules(), true);
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_privacySaveRequests.remove(keyTypeId);
|
||||
}).send();
|
||||
|
||||
_privacySaveRequests.emplace(keyTypeId, requestId);
|
||||
}
|
||||
|
||||
void UserPrivacy::apply(
|
||||
mtpTypeId type,
|
||||
const TLRules &rules,
|
||||
bool allLoaded) {
|
||||
if (const auto key = TLToKey(type)) {
|
||||
if (!allLoaded) {
|
||||
reload(*key);
|
||||
return;
|
||||
}
|
||||
pushPrivacy(*key, rules);
|
||||
if ((*key) == Key::LastSeen) {
|
||||
_session->api().updatePrivacyLastSeens();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UserPrivacy::reload(Key key) {
|
||||
if (_privacyRequestIds.contains(key)) {
|
||||
return;
|
||||
}
|
||||
const auto requestId = _api.request(MTPaccount_GetPrivacy(
|
||||
KeyToTL(key)
|
||||
)).done([=](const MTPaccount_PrivacyRules &result) {
|
||||
_privacyRequestIds.erase(key);
|
||||
result.match([&](const MTPDaccount_privacyRules &data) {
|
||||
_session->data().processUsers(data.vusers());
|
||||
_session->data().processChats(data.vchats());
|
||||
pushPrivacy(key, data.vrules());
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_privacyRequestIds.erase(key);
|
||||
}).send();
|
||||
_privacyRequestIds.emplace(key, requestId);
|
||||
}
|
||||
|
||||
void UserPrivacy::pushPrivacy(Key key, const TLRules &rules) {
|
||||
const auto &saved = (_privacyValues[key] =
|
||||
TLToRules(rules, _session->data()));
|
||||
const auto i = _privacyChanges.find(key);
|
||||
if (i != end(_privacyChanges)) {
|
||||
i->second.fire_copy(saved);
|
||||
}
|
||||
}
|
||||
|
||||
auto UserPrivacy::value(Key key) -> rpl::producer<UserPrivacy::Rule> {
|
||||
if (const auto i = _privacyValues.find(key); i != end(_privacyValues)) {
|
||||
return _privacyChanges[key].events_starting_with_copy(i->second);
|
||||
} else {
|
||||
return _privacyChanges[key].events();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
73
Telegram/SourceFiles/api/api_user_privacy.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
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 Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Api {
|
||||
|
||||
class UserPrivacy final {
|
||||
public:
|
||||
enum class Key {
|
||||
PhoneNumber,
|
||||
AddedByPhone,
|
||||
LastSeen,
|
||||
Calls,
|
||||
Invites,
|
||||
CallsPeer2Peer,
|
||||
Forwards,
|
||||
ProfilePhoto,
|
||||
};
|
||||
enum class Option {
|
||||
Everyone,
|
||||
Contacts,
|
||||
Nobody,
|
||||
};
|
||||
struct Rule {
|
||||
Option option = Option::Everyone;
|
||||
std::vector<not_null<PeerData*>> always;
|
||||
std::vector<not_null<PeerData*>> never;
|
||||
bool ignoreAlways = false;
|
||||
bool ignoreNever = false;
|
||||
};
|
||||
|
||||
explicit UserPrivacy(not_null<ApiWrap*> api);
|
||||
|
||||
void save(
|
||||
Key key,
|
||||
const UserPrivacy::Rule &rule);
|
||||
void apply(
|
||||
mtpTypeId type,
|
||||
const MTPVector<MTPPrivacyRule> &rules,
|
||||
bool allLoaded);
|
||||
|
||||
void reload(Key key);
|
||||
rpl::producer<Rule> value(Key key);
|
||||
|
||||
private:
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
void pushPrivacy(Key key, const MTPVector<MTPPrivacyRule> &rules);
|
||||
|
||||
base::flat_map<mtpTypeId, mtpRequestId> _privacySaveRequests;
|
||||
|
||||
base::flat_map<Key, mtpRequestId> _privacyRequestIds;
|
||||
base::flat_map<Key, Rule> _privacyValues;
|
||||
std::map<Key, rpl::event_stream<Rule>> _privacyChanges;
|
||||
|
||||
MTP::Sender _api;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Api
|
||||
@@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "api/api_authorizations.h"
|
||||
#include "api/api_attached_stickers.h"
|
||||
#include "api/api_blocked_peers.h"
|
||||
#include "api/api_cloud_password.h"
|
||||
#include "api/api_hash.h"
|
||||
#include "api/api_invite_links.h"
|
||||
#include "api/api_media.h"
|
||||
@@ -18,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_sensitive_content.h"
|
||||
#include "api/api_global_privacy.h"
|
||||
#include "api/api_updates.h"
|
||||
#include "api/api_user_privacy.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_changes.h"
|
||||
@@ -42,7 +45,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "dialogs/dialogs_key.h"
|
||||
#include "core/core_cloud_password.h"
|
||||
#include "core/application.h"
|
||||
#include "base/openssl_help.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "base/qt_adapters.h"
|
||||
#include "base/call_delayed.h"
|
||||
@@ -108,7 +110,6 @@ constexpr auto kStickersByEmojiInvalidateTimeout = crl::time(60 * 60 * 1000);
|
||||
constexpr auto kNotifySettingSaveTimeout = crl::time(1000);
|
||||
constexpr auto kDialogsFirstLoad = 20;
|
||||
constexpr auto kDialogsPerPage = 500;
|
||||
constexpr auto kBlockedFirstSlice = 16;
|
||||
|
||||
using PhotoFileLocationId = Data::PhotoFileLocationId;
|
||||
using DocumentFileLocationId = Data::DocumentFileLocationId;
|
||||
@@ -120,65 +121,6 @@ using UpdatedFileReferences = Data::UpdatedFileReferences;
|
||||
|
||||
} // namespace
|
||||
|
||||
MTPInputPrivacyKey ApiWrap::Privacy::Input(Key key) {
|
||||
switch (key) {
|
||||
case Privacy::Key::Calls: return MTP_inputPrivacyKeyPhoneCall();
|
||||
case Privacy::Key::Invites: return MTP_inputPrivacyKeyChatInvite();
|
||||
case Privacy::Key::PhoneNumber: return MTP_inputPrivacyKeyPhoneNumber();
|
||||
case Privacy::Key::AddedByPhone:
|
||||
return MTP_inputPrivacyKeyAddedByPhone();
|
||||
case Privacy::Key::LastSeen:
|
||||
return MTP_inputPrivacyKeyStatusTimestamp();
|
||||
case Privacy::Key::CallsPeer2Peer:
|
||||
return MTP_inputPrivacyKeyPhoneP2P();
|
||||
case Privacy::Key::Forwards:
|
||||
return MTP_inputPrivacyKeyForwards();
|
||||
case Privacy::Key::ProfilePhoto:
|
||||
return MTP_inputPrivacyKeyProfilePhoto();
|
||||
}
|
||||
Unexpected("Key in ApiWrap::Privacy::Input.");
|
||||
}
|
||||
|
||||
std::optional<ApiWrap::Privacy::Key> ApiWrap::Privacy::KeyFromMTP(
|
||||
mtpTypeId type) {
|
||||
using Key = Privacy::Key;
|
||||
switch (type) {
|
||||
case mtpc_privacyKeyPhoneNumber:
|
||||
case mtpc_inputPrivacyKeyPhoneNumber: return Key::PhoneNumber;
|
||||
case mtpc_privacyKeyAddedByPhone:
|
||||
case mtpc_inputPrivacyKeyAddedByPhone: return Key::AddedByPhone;
|
||||
case mtpc_privacyKeyStatusTimestamp:
|
||||
case mtpc_inputPrivacyKeyStatusTimestamp: return Key::LastSeen;
|
||||
case mtpc_privacyKeyChatInvite:
|
||||
case mtpc_inputPrivacyKeyChatInvite: return Key::Invites;
|
||||
case mtpc_privacyKeyPhoneCall:
|
||||
case mtpc_inputPrivacyKeyPhoneCall: return Key::Calls;
|
||||
case mtpc_privacyKeyPhoneP2P:
|
||||
case mtpc_inputPrivacyKeyPhoneP2P: return Key::CallsPeer2Peer;
|
||||
case mtpc_privacyKeyForwards:
|
||||
case mtpc_inputPrivacyKeyForwards: return Key::Forwards;
|
||||
case mtpc_privacyKeyProfilePhoto:
|
||||
case mtpc_inputPrivacyKeyProfilePhoto: return Key::ProfilePhoto;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool ApiWrap::BlockedPeersSlice::Item::operator==(const Item &other) const {
|
||||
return (peer == other.peer) && (date == other.date);
|
||||
}
|
||||
|
||||
bool ApiWrap::BlockedPeersSlice::Item::operator!=(const Item &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool ApiWrap::BlockedPeersSlice::operator==(const BlockedPeersSlice &other) const {
|
||||
return (total == other.total) && (list == other.list);
|
||||
}
|
||||
|
||||
bool ApiWrap::BlockedPeersSlice::operator!=(const BlockedPeersSlice &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||
: MTP::Sender(&session->account().mtp())
|
||||
, _session(session)
|
||||
@@ -192,9 +134,12 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||
, _updateNotifySettingsTimer([=] { sendNotifySettingsUpdates(); })
|
||||
, _authorizations(std::make_unique<Api::Authorizations>(this))
|
||||
, _attachedStickers(std::make_unique<Api::AttachedStickers>(this))
|
||||
, _blockedPeers(std::make_unique<Api::BlockedPeers>(this))
|
||||
, _cloudPassword(std::make_unique<Api::CloudPassword>(this))
|
||||
, _selfDestruct(std::make_unique<Api::SelfDestruct>(this))
|
||||
, _sensitiveContent(std::make_unique<Api::SensitiveContent>(this))
|
||||
, _globalPrivacy(std::make_unique<Api::GlobalPrivacy>(this))
|
||||
, _userPrivacy(std::make_unique<Api::UserPrivacy>(this))
|
||||
, _inviteLinks(std::make_unique<Api::InviteLinks>(this)) {
|
||||
crl::on_main(session, [=] {
|
||||
// You can't use _session->lifetime() in the constructor,
|
||||
@@ -2175,68 +2120,6 @@ void ApiWrap::leaveChannel(not_null<ChannelData*> channel) {
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::blockPeer(not_null<PeerData*> peer) {
|
||||
if (peer->isBlocked()) {
|
||||
session().changes().peerUpdated(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::IsBlocked);
|
||||
} 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([=](const MTP::Error &error) {
|
||||
_blockRequests.erase(peer);
|
||||
}).send();
|
||||
|
||||
_blockRequests.emplace(peer, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::unblockPeer(not_null<PeerData*> peer, Fn<void()> onDone) {
|
||||
if (!peer->isBlocked()) {
|
||||
session().changes().peerUpdated(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::IsBlocked);
|
||||
return;
|
||||
} else if (_blockRequests.find(peer) != end(_blockRequests)) {
|
||||
return;
|
||||
}
|
||||
const auto requestId = request(MTPcontacts_Unblock(
|
||||
peer->input
|
||||
)).done([=](const MTPBool &result) {
|
||||
_blockRequests.erase(peer);
|
||||
peer->setIsBlocked(false);
|
||||
if (_blockedPeersSlice) {
|
||||
auto &list = _blockedPeersSlice->list;
|
||||
for (auto i = list.begin(); i != list.end(); ++i) {
|
||||
if (i->peer == peer) {
|
||||
list.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_blockedPeersSlice->total > list.size()) {
|
||||
--_blockedPeersSlice->total;
|
||||
}
|
||||
_blockedPeersChanges.fire_copy(*_blockedPeersSlice);
|
||||
}
|
||||
if (onDone) {
|
||||
onDone();
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_blockRequests.erase(peer);
|
||||
}).send();
|
||||
_blockRequests.emplace(peer, requestId);
|
||||
}
|
||||
|
||||
void ApiWrap::requestNotifySettings(const MTPInputNotifyPeer &peer) {
|
||||
const auto key = [&] {
|
||||
switch (peer.type()) {
|
||||
@@ -2309,45 +2192,7 @@ void ApiWrap::saveDraftToCloudDelayed(not_null<History*> history) {
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::savePrivacy(
|
||||
const MTPInputPrivacyKey &key,
|
||||
QVector<MTPInputPrivacyRule> &&rules) {
|
||||
const auto keyTypeId = key.type();
|
||||
const auto it = _privacySaveRequests.find(keyTypeId);
|
||||
if (it != _privacySaveRequests.cend()) {
|
||||
request(it->second).cancel();
|
||||
_privacySaveRequests.erase(it);
|
||||
}
|
||||
|
||||
const auto requestId = request(MTPaccount_SetPrivacy(
|
||||
key,
|
||||
MTP_vector<MTPInputPrivacyRule>(std::move(rules))
|
||||
)).done([=](const MTPaccount_PrivacyRules &result) {
|
||||
result.match([&](const MTPDaccount_privacyRules &data) {
|
||||
_session->data().processUsers(data.vusers());
|
||||
_session->data().processChats(data.vchats());
|
||||
_privacySaveRequests.remove(keyTypeId);
|
||||
if (const auto key = Privacy::KeyFromMTP(keyTypeId)) {
|
||||
handlePrivacyChange(*key, data.vrules());
|
||||
}
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_privacySaveRequests.remove(keyTypeId);
|
||||
}).send();
|
||||
|
||||
_privacySaveRequests.emplace(keyTypeId, requestId);
|
||||
}
|
||||
|
||||
void ApiWrap::handlePrivacyChange(
|
||||
Privacy::Key key,
|
||||
const MTPVector<MTPPrivacyRule> &rules) {
|
||||
pushPrivacy(key, rules.v);
|
||||
if (key == Privacy::Key::LastSeen) {
|
||||
updatePrivacyLastSeens(rules.v);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::updatePrivacyLastSeens(const QVector<MTPPrivacyRule> &rules) {
|
||||
void ApiWrap::updatePrivacyLastSeens() {
|
||||
const auto now = base::unixtime::now();
|
||||
_session->data().enumerateUsers([&](UserData *user) {
|
||||
if (user->isSelf() || !user->isFullLoaded()) {
|
||||
@@ -3783,18 +3628,17 @@ void ApiWrap::forwardMessages(
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||
|
||||
auto flags = MTPDmessage::Flags(0);
|
||||
auto clientFlags = MTPDmessage_ClientFlags();
|
||||
auto flags = MessageFlags();
|
||||
auto sendFlags = MTPmessages_ForwardMessages::Flags(0);
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_ForwardMessages::Flag::f_silent;
|
||||
}
|
||||
if (action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
sendFlags |= MTPmessages_ForwardMessages::Flag::f_schedule_date;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
flags |= MessageFlag::LocalHistoryEntry;
|
||||
}
|
||||
|
||||
auto forwardFrom = items.front()->history()->peer;
|
||||
@@ -3861,7 +3705,6 @@ void ApiWrap::forwardMessages(
|
||||
history->addNewLocalMessage(
|
||||
newId.msg,
|
||||
flags,
|
||||
clientFlags,
|
||||
HistoryItem::NewMessageDate(action.options.scheduled),
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
@@ -3926,61 +3769,44 @@ void ApiWrap::sendSharedContact(
|
||||
_session->data().nextLocalMessageId());
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
|
||||
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto flags = NewMessageFlags(peer);
|
||||
if (action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to;
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
}
|
||||
const auto replyHeader = NewMessageReplyHeader(action);
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
flags |= MessageFlag::LocalHistoryEntry;
|
||||
}
|
||||
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),
|
||||
peerToMTP(messageFromId),
|
||||
peerToMTP(peer->id),
|
||||
MTPMessageFwdHeader(),
|
||||
MTPint(), // via_bot_id
|
||||
replyHeader,
|
||||
MTP_int(HistoryItem::NewMessageDate(action.options.scheduled)),
|
||||
MTP_string(),
|
||||
MTP_messageMediaContact(
|
||||
MTP_string(phone),
|
||||
MTP_string(firstName),
|
||||
MTP_string(lastName),
|
||||
MTP_string(vcard),
|
||||
MTP_int(userId.bare)), // #TODO ids
|
||||
MTPReplyMarkup(),
|
||||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(views),
|
||||
MTP_int(forwards),
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTP_string(messagePostAuthor),
|
||||
MTPlong(),
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>(),
|
||||
MTPint()), // ttl_period
|
||||
clientFlags,
|
||||
NewMessageType::Unread);
|
||||
const auto viaBotId = UserId();
|
||||
const auto item = history->addNewLocalMessage(
|
||||
newId.msg,
|
||||
flags,
|
||||
viaBotId,
|
||||
action.replyTo,
|
||||
HistoryItem::NewMessageDate(action.options.scheduled),
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
TextWithEntities(),
|
||||
MTP_messageMediaContact(
|
||||
MTP_string(phone),
|
||||
MTP_string(firstName),
|
||||
MTP_string(lastName),
|
||||
MTP_string(), // vcard
|
||||
MTP_int(userId.bare)), // #TODO ids
|
||||
MTPReplyMarkup());
|
||||
|
||||
const auto media = MTP_inputMediaContact(
|
||||
MTP_string(phone),
|
||||
MTP_string(firstName),
|
||||
MTP_string(lastName),
|
||||
MTP_string(vcard));
|
||||
MTP_string()); // vcard
|
||||
sendMedia(item, media, action.options);
|
||||
|
||||
_session->data().sendHistoryChangeNotifications();
|
||||
@@ -4181,11 +4007,10 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
_session->data().registerMessageSentData(randomId, peer->id, sending.text);
|
||||
|
||||
MTPstring msgText(MTP_string(sending.text));
|
||||
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_entities;
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto flags = NewMessageFlags(peer);
|
||||
auto sendFlags = MTPmessages_SendMessage::Flags(0);
|
||||
if (action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to;
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
const auto replyHeader = NewMessageReplyHeader(action);
|
||||
@@ -4198,7 +4023,6 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
MTP_webPagePending(
|
||||
MTP_long(page->id),
|
||||
MTP_int(page->pendingTill)));
|
||||
flags |= MTPDmessage::Flag::f_media;
|
||||
}
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||
@@ -4206,10 +4030,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_silent;
|
||||
}
|
||||
auto localEntities = Api::EntitiesToMTP(
|
||||
_session,
|
||||
sending.entities);
|
||||
auto sentEntities = Api::EntitiesToMTP(
|
||||
const auto sentEntities = Api::EntitiesToMTP(
|
||||
_session,
|
||||
sending.entities,
|
||||
Api::ConvertOption::SkipLocal);
|
||||
@@ -4227,39 +4048,23 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
? _session->user()->name
|
||||
: QString();
|
||||
if (action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
sendFlags |= MTPmessages_SendMessage::Flag::f_schedule_date;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
flags |= MessageFlag::LocalHistoryEntry;
|
||||
}
|
||||
const auto views = 1;
|
||||
const auto forwards = 0;
|
||||
lastMessage = history->addNewMessage(
|
||||
MTP_message(
|
||||
MTP_flags(flags),
|
||||
MTP_int(newId.msg),
|
||||
peerToMTP(messageFromId),
|
||||
peerToMTP(peer->id),
|
||||
MTPMessageFwdHeader(),
|
||||
MTPint(), // via_bot_id
|
||||
replyHeader,
|
||||
MTP_int(
|
||||
HistoryItem::NewMessageDate(action.options.scheduled)),
|
||||
msgText,
|
||||
media,
|
||||
MTPReplyMarkup(),
|
||||
localEntities,
|
||||
MTP_int(views),
|
||||
MTP_int(forwards),
|
||||
MTPMessageReplies(),
|
||||
MTPint(), // edit_date
|
||||
MTP_string(messagePostAuthor),
|
||||
MTPlong(),
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>(),
|
||||
MTPint()), // ttl_period
|
||||
clientFlags,
|
||||
NewMessageType::Unread);
|
||||
const auto viaBotId = UserId();
|
||||
lastMessage = history->addNewLocalMessage(
|
||||
newId.msg,
|
||||
flags,
|
||||
viaBotId,
|
||||
action.replyTo,
|
||||
HistoryItem::NewMessageDate(action.options.scheduled),
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
sending,
|
||||
media,
|
||||
MTPReplyMarkup());
|
||||
histories.sendRequest(history, requestType, [=](Fn<void()> finish) {
|
||||
history->sendRequestId = request(MTPmessages_SendMessage(
|
||||
MTP_flags(sendFlags),
|
||||
@@ -4346,11 +4151,10 @@ void ApiWrap::sendInlineResult(
|
||||
_session->data().nextLocalMessageId());
|
||||
const auto randomId = openssl::RandomValue<uint64>();
|
||||
|
||||
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto flags = NewMessageFlags(peer);
|
||||
auto sendFlags = MTPmessages_SendInlineBotResult::Flag::f_clear_draft | 0;
|
||||
if (action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to;
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
@@ -4360,13 +4164,13 @@ void ApiWrap::sendInlineResult(
|
||||
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_silent;
|
||||
}
|
||||
if (bot) {
|
||||
flags |= MTPDmessage::Flag::f_via_bot_id;
|
||||
flags |= MessageFlag::HasViaBot;
|
||||
}
|
||||
if (action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_schedule_date;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
flags |= MessageFlag::LocalHistoryEntry;
|
||||
}
|
||||
|
||||
const auto messageFromId = anonymousPost ? 0 : _session->userPeerId();
|
||||
@@ -4379,10 +4183,9 @@ void ApiWrap::sendInlineResult(
|
||||
data->addToHistory(
|
||||
history,
|
||||
flags,
|
||||
clientFlags,
|
||||
newId.msg,
|
||||
messageFromId,
|
||||
MTP_int(HistoryItem::NewMessageDate(action.options.scheduled)),
|
||||
HistoryItem::NewMessageDate(action.options.scheduled),
|
||||
bot ? peerToUser(bot->id) : 0,
|
||||
action.replyTo,
|
||||
messagePostAuthor);
|
||||
@@ -4784,52 +4587,6 @@ void ApiWrap::clearPeerPhoto(not_null<PhotoData*> photo) {
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::reloadPasswordState() {
|
||||
if (_passwordRequestId) {
|
||||
return;
|
||||
}
|
||||
_passwordRequestId = request(MTPaccount_GetPassword(
|
||||
)).done([=](const MTPaccount_Password &result) {
|
||||
_passwordRequestId = 0;
|
||||
result.match([&](const MTPDaccount_password &data) {
|
||||
openssl::AddRandomSeed(bytes::make_span(data.vsecure_random().v));
|
||||
if (_passwordState) {
|
||||
*_passwordState = Core::ParseCloudPasswordState(data);
|
||||
} else {
|
||||
_passwordState = std::make_unique<Core::CloudPasswordState>(
|
||||
Core::ParseCloudPasswordState(data));
|
||||
}
|
||||
_passwordStateChanges.fire_copy(*_passwordState);
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_passwordRequestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::clearUnconfirmedPassword() {
|
||||
_passwordRequestId = request(MTPaccount_CancelPasswordEmail(
|
||||
)).done([=](const MTPBool &result) {
|
||||
_passwordRequestId = 0;
|
||||
reloadPasswordState();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_passwordRequestId = 0;
|
||||
reloadPasswordState();
|
||||
}).send();
|
||||
}
|
||||
|
||||
rpl::producer<Core::CloudPasswordState> ApiWrap::passwordState() const {
|
||||
return _passwordState
|
||||
? _passwordStateChanges.events_starting_with_copy(*_passwordState)
|
||||
: (_passwordStateChanges.events() | rpl::type_erased());
|
||||
}
|
||||
|
||||
auto ApiWrap::passwordStateCurrent() const
|
||||
-> std::optional<Core::CloudPasswordState> {
|
||||
return _passwordState
|
||||
? base::make_optional(*_passwordState)
|
||||
: std::nullopt;
|
||||
}
|
||||
|
||||
void ApiWrap::reloadContactSignupSilent() {
|
||||
if (_contactSignupSilentRequestId) {
|
||||
return;
|
||||
@@ -4903,176 +4660,6 @@ void ApiWrap::saveSelfBio(const QString &text, FnMut<void()> done) {
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::reloadPrivacy(Privacy::Key key) {
|
||||
if (_privacyRequestIds.contains(key)) {
|
||||
return;
|
||||
}
|
||||
const auto requestId = request(MTPaccount_GetPrivacy(
|
||||
Privacy::Input(key)
|
||||
)).done([=](const MTPaccount_PrivacyRules &result) {
|
||||
_privacyRequestIds.erase(key);
|
||||
result.match([&](const MTPDaccount_privacyRules &data) {
|
||||
_session->data().processUsers(data.vusers());
|
||||
_session->data().processChats(data.vchats());
|
||||
pushPrivacy(key, data.vrules().v);
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_privacyRequestIds.erase(key);
|
||||
}).send();
|
||||
_privacyRequestIds.emplace(key, requestId);
|
||||
}
|
||||
|
||||
auto ApiWrap::parsePrivacy(const QVector<MTPPrivacyRule> &rules)
|
||||
-> Privacy {
|
||||
using Option = Privacy::Option;
|
||||
|
||||
// This is simplified version of privacy rules interpretation.
|
||||
// But it should be fine for all the apps
|
||||
// that use the same subset of features.
|
||||
auto result = Privacy();
|
||||
auto optionSet = false;
|
||||
const auto SetOption = [&](Option option) {
|
||||
if (optionSet) return;
|
||||
optionSet = true;
|
||||
result.option = option;
|
||||
};
|
||||
auto &always = result.always;
|
||||
auto &never = result.never;
|
||||
const auto Feed = [&](const MTPPrivacyRule &rule) {
|
||||
rule.match([&](const MTPDprivacyValueAllowAll &) {
|
||||
SetOption(Option::Everyone);
|
||||
}, [&](const MTPDprivacyValueAllowContacts &) {
|
||||
SetOption(Option::Contacts);
|
||||
}, [&](const MTPDprivacyValueAllowUsers &data) {
|
||||
const auto &users = data.vusers().v;
|
||||
always.reserve(always.size() + users.size());
|
||||
for (const auto userId : users) {
|
||||
const auto user = _session->data().user(UserId(userId.v));
|
||||
if (!base::contains(never, user)
|
||||
&& !base::contains(always, user)) {
|
||||
always.emplace_back(user);
|
||||
}
|
||||
}
|
||||
}, [&](const MTPDprivacyValueAllowChatParticipants &data) {
|
||||
const auto &chats = data.vchats().v;
|
||||
always.reserve(always.size() + chats.size());
|
||||
for (const auto &chatId : chats) {
|
||||
const auto chat = _session->data().chatLoaded(chatId);
|
||||
const auto peer = chat
|
||||
? static_cast<PeerData*>(chat)
|
||||
: _session->data().channelLoaded(chatId);
|
||||
if (peer
|
||||
&& !base::contains(never, peer)
|
||||
&& !base::contains(always, peer)) {
|
||||
always.emplace_back(peer);
|
||||
}
|
||||
}
|
||||
}, [&](const MTPDprivacyValueDisallowContacts &) {
|
||||
// not supported
|
||||
}, [&](const MTPDprivacyValueDisallowAll &) {
|
||||
SetOption(Option::Nobody);
|
||||
}, [&](const MTPDprivacyValueDisallowUsers &data) {
|
||||
const auto &users = data.vusers().v;
|
||||
never.reserve(never.size() + users.size());
|
||||
for (const auto userId : users) {
|
||||
const auto user = _session->data().user(UserId(userId.v));
|
||||
if (!base::contains(always, user)
|
||||
&& !base::contains(never, user)) {
|
||||
never.emplace_back(user);
|
||||
}
|
||||
}
|
||||
}, [&](const MTPDprivacyValueDisallowChatParticipants &data) {
|
||||
const auto &chats = data.vchats().v;
|
||||
never.reserve(never.size() + chats.size());
|
||||
for (const auto &chatId : chats) {
|
||||
const auto chat = _session->data().chatLoaded(chatId);
|
||||
const auto peer = chat
|
||||
? static_cast<PeerData*>(chat)
|
||||
: _session->data().channelLoaded(chatId);
|
||||
if (peer
|
||||
&& !base::contains(always, peer)
|
||||
&& !base::contains(never, peer)) {
|
||||
never.emplace_back(peer);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
for (const auto &rule : rules) {
|
||||
Feed(rule);
|
||||
}
|
||||
Feed(MTP_privacyValueDisallowAll()); // disallow by default.
|
||||
return result;
|
||||
}
|
||||
|
||||
void ApiWrap::pushPrivacy(
|
||||
Privacy::Key key,
|
||||
const QVector<MTPPrivacyRule> &rules) {
|
||||
const auto &saved = (_privacyValues[key] = parsePrivacy(rules));
|
||||
const auto i = _privacyChanges.find(key);
|
||||
if (i != end(_privacyChanges)) {
|
||||
i->second.fire_copy(saved);
|
||||
}
|
||||
}
|
||||
|
||||
auto ApiWrap::privacyValue(Privacy::Key key) -> rpl::producer<Privacy> {
|
||||
if (const auto i = _privacyValues.find(key); i != end(_privacyValues)) {
|
||||
return _privacyChanges[key].events_starting_with_copy(i->second);
|
||||
} else {
|
||||
return _privacyChanges[key].events();
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::reloadBlockedPeers() {
|
||||
if (_blockedPeersRequestId) {
|
||||
return;
|
||||
}
|
||||
_blockedPeersRequestId = request(MTPcontacts_GetBlocked(
|
||||
MTP_int(0),
|
||||
MTP_int(kBlockedFirstSlice)
|
||||
)).done([=](const MTPcontacts_Blocked &result) {
|
||||
_blockedPeersRequestId = 0;
|
||||
const auto push = [&](
|
||||
int count,
|
||||
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 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 (!_blockedPeersSlice || *_blockedPeersSlice != slice) {
|
||||
_blockedPeersSlice = slice;
|
||||
_blockedPeersChanges.fire(std::move(slice));
|
||||
}
|
||||
};
|
||||
result.match([&](const MTPDcontacts_blockedSlice &data) {
|
||||
_session->data().processUsers(data.vusers());
|
||||
push(data.vcount().v, data.vblocked().v);
|
||||
}, [&](const MTPDcontacts_blocked &data) {
|
||||
_session->data().processUsers(data.vusers());
|
||||
push(0, data.vblocked().v);
|
||||
});
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_blockedPeersRequestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
auto ApiWrap::blockedPeersSlice() -> rpl::producer<BlockedPeersSlice> {
|
||||
if (!_blockedPeersSlice) {
|
||||
reloadBlockedPeers();
|
||||
}
|
||||
return _blockedPeersSlice
|
||||
? _blockedPeersChanges.events_starting_with_copy(*_blockedPeersSlice)
|
||||
: (_blockedPeersChanges.events() | rpl::type_erased());
|
||||
}
|
||||
|
||||
Api::Authorizations &ApiWrap::authorizations() {
|
||||
return *_authorizations;
|
||||
}
|
||||
@@ -5081,6 +4668,14 @@ Api::AttachedStickers &ApiWrap::attachedStickers() {
|
||||
return *_attachedStickers;
|
||||
}
|
||||
|
||||
Api::BlockedPeers &ApiWrap::blockedPeers() {
|
||||
return *_blockedPeers;
|
||||
}
|
||||
|
||||
Api::CloudPassword &ApiWrap::cloudPassword() {
|
||||
return *_cloudPassword;
|
||||
}
|
||||
|
||||
Api::SelfDestruct &ApiWrap::selfDestruct() {
|
||||
return *_selfDestruct;
|
||||
}
|
||||
@@ -5093,6 +4688,10 @@ Api::GlobalPrivacy &ApiWrap::globalPrivacy() {
|
||||
return *_globalPrivacy;
|
||||
}
|
||||
|
||||
Api::UserPrivacy &ApiWrap::userPrivacy() {
|
||||
return *_userPrivacy;
|
||||
}
|
||||
|
||||
Api::InviteLinks &ApiWrap::inviteLinks() {
|
||||
return *_inviteLinks;
|
||||
}
|
||||
|
||||
@@ -45,10 +45,6 @@ namespace Dialogs {
|
||||
class Key;
|
||||
} // namespace Dialogs
|
||||
|
||||
namespace Core {
|
||||
struct CloudPasswordState;
|
||||
} // namespace Core
|
||||
|
||||
namespace Ui {
|
||||
struct PreparedList;
|
||||
} // namespace Ui
|
||||
@@ -58,9 +54,12 @@ namespace Api {
|
||||
class Updates;
|
||||
class Authorizations;
|
||||
class AttachedStickers;
|
||||
class BlockedPeers;
|
||||
class CloudPassword;
|
||||
class SelfDestruct;
|
||||
class SensitiveContent;
|
||||
class GlobalPrivacy;
|
||||
class UserPrivacy;
|
||||
class InviteLinks;
|
||||
|
||||
namespace details {
|
||||
@@ -113,46 +112,6 @@ public:
|
||||
using SendAction = Api::SendAction;
|
||||
using MessageToSend = Api::MessageToSend;
|
||||
|
||||
struct Privacy {
|
||||
enum class Key {
|
||||
PhoneNumber,
|
||||
AddedByPhone,
|
||||
LastSeen,
|
||||
Calls,
|
||||
Invites,
|
||||
CallsPeer2Peer,
|
||||
Forwards,
|
||||
ProfilePhoto,
|
||||
};
|
||||
enum class Option {
|
||||
Everyone,
|
||||
Contacts,
|
||||
Nobody,
|
||||
};
|
||||
Option option = Option::Everyone;
|
||||
std::vector<not_null<PeerData*>> always;
|
||||
std::vector<not_null<PeerData*>> never;
|
||||
|
||||
static MTPInputPrivacyKey Input(Key key);
|
||||
static std::optional<Key> KeyFromMTP(mtpTypeId type);
|
||||
};
|
||||
|
||||
struct BlockedPeersSlice {
|
||||
struct Item {
|
||||
PeerData *peer = nullptr;
|
||||
TimeId date = 0;
|
||||
|
||||
bool operator==(const Item &other) const;
|
||||
bool operator!=(const Item &other) const;
|
||||
};
|
||||
|
||||
QVector<Item> list;
|
||||
int total = 0;
|
||||
|
||||
bool operator==(const BlockedPeersSlice &other) const;
|
||||
bool operator!=(const BlockedPeersSlice &other) const;
|
||||
};
|
||||
|
||||
explicit ApiWrap(not_null<Main::Session*> session);
|
||||
~ApiWrap();
|
||||
|
||||
@@ -295,19 +254,10 @@ public:
|
||||
void joinChannel(not_null<ChannelData*> channel);
|
||||
void leaveChannel(not_null<ChannelData*> channel);
|
||||
|
||||
void blockPeer(not_null<PeerData*> peer);
|
||||
void unblockPeer(not_null<PeerData*> peer, Fn<void()> onDone = nullptr);
|
||||
|
||||
void requestNotifySettings(const MTPInputNotifyPeer &peer);
|
||||
void updateNotifySettingsDelayed(not_null<const PeerData*> peer);
|
||||
void saveDraftToCloudDelayed(not_null<History*> history);
|
||||
|
||||
void savePrivacy(
|
||||
const MTPInputPrivacyKey &key,
|
||||
QVector<MTPInputPrivacyRule> &&rules);
|
||||
void handlePrivacyChange(
|
||||
Privacy::Key key,
|
||||
const MTPVector<MTPPrivacyRule> &rules);
|
||||
static int OnlineTillFromStatus(
|
||||
const MTPUserStatus &status,
|
||||
int currentOnlineTill);
|
||||
@@ -433,11 +383,6 @@ public:
|
||||
void uploadPeerPhoto(not_null<PeerData*> peer, QImage &&image);
|
||||
void clearPeerPhoto(not_null<PhotoData*> photo);
|
||||
|
||||
void reloadPasswordState();
|
||||
void clearUnconfirmedPassword();
|
||||
rpl::producer<Core::CloudPasswordState> passwordState() const;
|
||||
std::optional<Core::CloudPasswordState> passwordStateCurrent() const;
|
||||
|
||||
void reloadContactSignupSilent();
|
||||
rpl::producer<bool> contactSignupSilent() const;
|
||||
std::optional<bool> contactSignupSilentCurrent() const;
|
||||
@@ -445,17 +390,14 @@ public:
|
||||
|
||||
void saveSelfBio(const QString &text, FnMut<void()> done);
|
||||
|
||||
void reloadPrivacy(Privacy::Key key);
|
||||
rpl::producer<Privacy> privacyValue(Privacy::Key key);
|
||||
|
||||
void reloadBlockedPeers();
|
||||
rpl::producer<BlockedPeersSlice> blockedPeersSlice();
|
||||
|
||||
[[nodiscard]] Api::Authorizations &authorizations();
|
||||
[[nodiscard]] Api::AttachedStickers &attachedStickers();
|
||||
[[nodiscard]] Api::BlockedPeers &blockedPeers();
|
||||
[[nodiscard]] Api::CloudPassword &cloudPassword();
|
||||
[[nodiscard]] Api::SelfDestruct &selfDestruct();
|
||||
[[nodiscard]] Api::SensitiveContent &sensitiveContent();
|
||||
[[nodiscard]] Api::GlobalPrivacy &globalPrivacy();
|
||||
[[nodiscard]] Api::UserPrivacy &userPrivacy();
|
||||
[[nodiscard]] Api::InviteLinks &inviteLinks();
|
||||
|
||||
void createPoll(
|
||||
@@ -469,6 +411,8 @@ public:
|
||||
void closePoll(not_null<HistoryItem*> item);
|
||||
void reloadPollResults(not_null<HistoryItem*> item);
|
||||
|
||||
void updatePrivacyLastSeens();
|
||||
|
||||
private:
|
||||
struct MessageDataRequest {
|
||||
using Callbacks = QList<RequestMessageDataCallback>;
|
||||
@@ -625,12 +569,6 @@ private:
|
||||
|
||||
void photoUploadReady(const FullMsgId &msgId, const MTPInputFile &file);
|
||||
|
||||
Privacy parsePrivacy(const QVector<MTPPrivacyRule> &rules);
|
||||
void pushPrivacy(
|
||||
Privacy::Key key,
|
||||
const QVector<MTPPrivacyRule> &rules);
|
||||
void updatePrivacyLastSeens(const QVector<MTPPrivacyRule> &rules);
|
||||
|
||||
void migrateDone(
|
||||
not_null<PeerData*> peer,
|
||||
not_null<ChannelData*> channel);
|
||||
@@ -675,7 +613,6 @@ private:
|
||||
QMap<uint64, QPair<uint64, mtpRequestId> > _stickerSetRequests;
|
||||
|
||||
QMap<ChannelData*, mtpRequestId> _channelAmInRequests;
|
||||
base::flat_map<not_null<PeerData*>, mtpRequestId> _blockRequests;
|
||||
base::flat_map<PeerId, mtpRequestId> _notifySettingRequests;
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _draftsSaveRequestIds;
|
||||
base::Timer _draftsSaveTimer;
|
||||
@@ -700,8 +637,6 @@ private:
|
||||
|
||||
base::flat_map<not_null<EmojiPtr>, StickersByEmoji> _stickersByEmoji;
|
||||
|
||||
base::flat_map<mtpTypeId, mtpRequestId> _privacySaveRequests;
|
||||
|
||||
mtpRequestId _contactsRequestId = 0;
|
||||
mtpRequestId _contactsStatusesRequestId = 0;
|
||||
|
||||
@@ -764,27 +699,18 @@ private:
|
||||
|
||||
base::flat_map<FullMsgId, not_null<PeerData*>> _peerPhotoUploads;
|
||||
|
||||
mtpRequestId _passwordRequestId = 0;
|
||||
std::unique_ptr<Core::CloudPasswordState> _passwordState;
|
||||
rpl::event_stream<Core::CloudPasswordState> _passwordStateChanges;
|
||||
|
||||
mtpRequestId _saveBioRequestId = 0;
|
||||
FnMut<void()> _saveBioDone;
|
||||
QString _saveBioText;
|
||||
|
||||
base::flat_map<Privacy::Key, mtpRequestId> _privacyRequestIds;
|
||||
base::flat_map<Privacy::Key, Privacy> _privacyValues;
|
||||
std::map<Privacy::Key, rpl::event_stream<Privacy>> _privacyChanges;
|
||||
|
||||
mtpRequestId _blockedPeersRequestId = 0;
|
||||
std::optional<BlockedPeersSlice> _blockedPeersSlice;
|
||||
rpl::event_stream<BlockedPeersSlice> _blockedPeersChanges;
|
||||
|
||||
const std::unique_ptr<Api::Authorizations> _authorizations;
|
||||
const std::unique_ptr<Api::AttachedStickers> _attachedStickers;
|
||||
const std::unique_ptr<Api::BlockedPeers> _blockedPeers;
|
||||
const std::unique_ptr<Api::CloudPassword> _cloudPassword;
|
||||
const std::unique_ptr<Api::SelfDestruct> _selfDestruct;
|
||||
const std::unique_ptr<Api::SensitiveContent> _sensitiveContent;
|
||||
const std::unique_ptr<Api::GlobalPrivacy> _globalPrivacy;
|
||||
const std::unique_ptr<Api::UserPrivacy> _userPrivacy;
|
||||
const std::unique_ptr<Api::InviteLinks> _inviteLinks;
|
||||
|
||||
base::flat_map<FullMsgId, mtpRequestId> _pollVotesRequestIds;
|
||||
|
||||
@@ -38,7 +38,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "apiwrap.h"
|
||||
#include "numbers.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_overview.h"
|
||||
@@ -70,32 +69,6 @@ HistoryView::Element *hoveredItem = nullptr,
|
||||
|
||||
namespace App {
|
||||
|
||||
QString formatPhone(QString phone) {
|
||||
if (phone.isEmpty()) return QString();
|
||||
if (phone.at(0) == '0') return phone;
|
||||
|
||||
QString number = phone;
|
||||
for (const QChar *ch = phone.constData(), *e = ch + phone.size(); ch != e; ++ch) {
|
||||
if (ch->unicode() < '0' || ch->unicode() > '9') {
|
||||
number = phone.replace(QRegularExpression(qsl("[^\\d]")), QString());
|
||||
}
|
||||
}
|
||||
QVector<int> groups = phoneNumberParse(number);
|
||||
if (groups.isEmpty()) return '+' + number;
|
||||
|
||||
QString result;
|
||||
result.reserve(number.size() + groups.size() + 1);
|
||||
result.append('+');
|
||||
int32 sum = 0;
|
||||
for (int32 i = 0, l = groups.size(); i < l; ++i) {
|
||||
result.append(number.midRef(sum, groups.at(i)));
|
||||
sum += groups.at(i);
|
||||
if (sum < number.size()) result.append(' ');
|
||||
}
|
||||
if (sum < number.size()) result.append(number.midRef(sum));
|
||||
return result;
|
||||
}
|
||||
|
||||
void initMedia() {
|
||||
Ui::StartCachedCorners();
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ class Element;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace App {
|
||||
QString formatPhone(QString phone);
|
||||
|
||||
void hoveredItem(HistoryView::Element *item);
|
||||
HistoryView::Element *hoveredItem();
|
||||
void pressedItem(HistoryView::Element *item);
|
||||
|
||||
@@ -93,8 +93,6 @@ void AboutBox::showVersionHistory() {
|
||||
url += qsl("win/%1.zip");
|
||||
} else if (Platform::IsWindows64Bit()) {
|
||||
url += qsl("win64/%1.zip");
|
||||
} else if (Platform::IsOSXBuild()) {
|
||||
url += qsl("osx/%1.zip");
|
||||
} else if (Platform::IsMac()) {
|
||||
url += qsl("mac/%1.zip");
|
||||
} else if (Platform::IsLinux32Bit()) {
|
||||
|
||||
@@ -286,24 +286,25 @@ AdminLog::OwnedItem GenerateTextItem(
|
||||
bool out) {
|
||||
Expects(history->peer->isUser());
|
||||
|
||||
using Flag = MTPDmessage::Flag;
|
||||
static auto id = ServerMaxMsgId + (ServerMaxMsgId / 3);
|
||||
const auto flags = Flag::f_entities
|
||||
| Flag::f_from_id
|
||||
| (out ? Flag::f_out : Flag(0));
|
||||
const auto clientFlags = MTPDmessage_ClientFlag::f_fake_history_item;
|
||||
const auto replyTo = 0;
|
||||
const auto viaBotId = UserId(0);
|
||||
const auto flags = MessageFlag::FakeHistoryItem
|
||||
| MessageFlag::HasFromId
|
||||
| (out ? MessageFlag::Outgoing : MessageFlag(0));
|
||||
const auto replyTo = MsgId();
|
||||
const auto viaBotId = UserId();
|
||||
const auto groupedId = uint64();
|
||||
const auto item = history->makeMessage(
|
||||
++id,
|
||||
flags,
|
||||
clientFlags,
|
||||
replyTo,
|
||||
viaBotId,
|
||||
base::unixtime::now(),
|
||||
out ? history->session().userId() : peerToUser(history->peer->id),
|
||||
QString(),
|
||||
TextWithEntities{ TextUtilities::Clean(text) });
|
||||
TextWithEntities{ TextUtilities::Clean(text) },
|
||||
MTP_messageMediaEmpty(),
|
||||
MTPReplyMarkup(),
|
||||
groupedId);
|
||||
return AdminLog::OwnedItem(delegate, item);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/text/format_values.h" // Ui::FormatPhone
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/special_fields.h"
|
||||
#include "boxes/confirm_phone_box.h"
|
||||
@@ -21,7 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_user.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "apiwrap.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
@@ -235,7 +235,7 @@ void ChangePhoneBox::EnterPhone::sendPhoneFail(const MTP::Error &error, const QS
|
||||
tr::lng_change_phone_occupied(
|
||||
tr::now,
|
||||
lt_phone,
|
||||
App::formatPhone(phoneNumber)),
|
||||
Ui::FormatPhone(phoneNumber)),
|
||||
tr::lng_box_ok(tr::now)));
|
||||
} else {
|
||||
showError(Lang::Hard::ServerError());
|
||||
@@ -271,7 +271,7 @@ void ChangePhoneBox::EnterCode::prepare() {
|
||||
auto descriptionText = tr::lng_change_phone_code_description(
|
||||
tr::now,
|
||||
lt_phone,
|
||||
Ui::Text::Bold(App::formatPhone(_phone)),
|
||||
Ui::Text::Bold(Ui::FormatPhone(_phone)),
|
||||
Ui::Text::WithEntities);
|
||||
auto description = object_ptr<Ui::FlatLabel>(this, rpl::single(descriptionText), st::changePhoneLabel);
|
||||
description->moveToLeft(st::boxPadding.left(), 0);
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/text/format_values.h" // Ui::FormatPhone
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "core/click_handler_types.h" // UrlClickHandler
|
||||
#include "base/qthelp_url.h" // qthelp::url_encode
|
||||
@@ -18,7 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "numbers.h"
|
||||
#include "app.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mtproto/facade.h"
|
||||
#include "styles/style_layers.h"
|
||||
@@ -296,7 +296,7 @@ void ConfirmPhoneBox::prepare() {
|
||||
this,
|
||||
tr::lng_confirm_phone_about(
|
||||
lt_phone,
|
||||
rpl::single(Ui::Text::Bold(App::formatPhone(_phone))),
|
||||
rpl::single(Ui::Text::Bold(Ui::FormatPhone(_phone))),
|
||||
Ui::Text::WithEntities),
|
||||
st::confirmPhoneAboutLabel);
|
||||
|
||||
@@ -347,7 +347,7 @@ void ConfirmPhoneBox::sendCode() {
|
||||
|
||||
void ConfirmPhoneBox::confirmDone(const MTPBool &result) {
|
||||
_sendCodeRequestId = 0;
|
||||
Ui::show(Box<InformBox>(tr::lng_confirm_phone_success(tr::now, lt_phone, App::formatPhone(_phone))));
|
||||
Ui::show(Box<InformBox>(tr::lng_confirm_phone_success(tr::now, lt_phone, Ui::FormatPhone(_phone))));
|
||||
}
|
||||
|
||||
void ConfirmPhoneBox::confirmFail(const MTP::Error &error) {
|
||||
|
||||
@@ -170,63 +170,6 @@ void EditPrivacyBox::editExceptions(
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
QVector<MTPInputPrivacyRule> EditPrivacyBox::collectResult() {
|
||||
const auto collectInputUsers = [](const auto &peers) {
|
||||
auto result = QVector<MTPInputUser>();
|
||||
result.reserve(peers.size());
|
||||
for (const auto peer : peers) {
|
||||
if (const auto user = peer->asUser()) {
|
||||
result.push_back(user->inputUser);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const auto collectInputChats = [](const auto &peers) {
|
||||
auto result = QVector<MTPint>(); // #TODO ids
|
||||
result.reserve(peers.size());
|
||||
for (const auto peer : peers) {
|
||||
if (!peer->isUser()) {
|
||||
result.push_back(peerToBareMTPInt(peer->id));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
constexpr auto kMaxRules = 3; // allow users, disallow users, option
|
||||
auto result = QVector<MTPInputPrivacyRule>();
|
||||
result.reserve(kMaxRules);
|
||||
if (showExceptionLink(Exception::Always)) {
|
||||
const auto users = collectInputUsers(_value.always);
|
||||
const auto chats = collectInputChats(_value.always);
|
||||
if (!users.empty()) {
|
||||
result.push_back(MTP_inputPrivacyValueAllowUsers(MTP_vector<MTPInputUser>(users)));
|
||||
}
|
||||
if (!chats.empty()) {
|
||||
result.push_back(MTP_inputPrivacyValueAllowChatParticipants(MTP_vector<MTPint>(chats)));
|
||||
}
|
||||
}
|
||||
if (showExceptionLink(Exception::Never)) {
|
||||
const auto users = collectInputUsers(_value.never);
|
||||
const auto chats = collectInputChats(_value.never);
|
||||
if (!users.empty()) {
|
||||
result.push_back(MTP_inputPrivacyValueDisallowUsers(MTP_vector<MTPInputUser>(users)));
|
||||
}
|
||||
if (!chats.empty()) {
|
||||
result.push_back(MTP_inputPrivacyValueDisallowChatParticipants(MTP_vector<MTPint>(chats)));
|
||||
}
|
||||
}
|
||||
result.push_back([&] {
|
||||
switch (_value.option) {
|
||||
case Option::Everyone: return MTP_inputPrivacyValueAllowAll();
|
||||
case Option::Contacts: return MTP_inputPrivacyValueAllowContacts();
|
||||
case Option::Nobody: return MTP_inputPrivacyValueDisallowAll();
|
||||
}
|
||||
Unexpected("Option value in EditPrivacyBox::collectResult.");
|
||||
}());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<not_null<PeerData*>> &EditPrivacyBox::exceptions(Exception exception) {
|
||||
switch (exception) {
|
||||
case Exception::Always: return _value.always;
|
||||
@@ -379,10 +322,13 @@ void EditPrivacyBox::setupContent() {
|
||||
const auto someAreDisallowed = (_value.option != Option::Everyone)
|
||||
|| !_value.never.empty();
|
||||
_controller->confirmSave(someAreDisallowed, crl::guard(this, [=] {
|
||||
_value.ignoreAlways = !showExceptionLink(Exception::Always);
|
||||
_value.ignoreNever = !showExceptionLink(Exception::Never);
|
||||
|
||||
_controller->saveAdditional();
|
||||
_window->session().api().savePrivacy(
|
||||
_controller->apiKey(),
|
||||
collectResult());
|
||||
_window->session().api().userPrivacy().save(
|
||||
_controller->key(),
|
||||
_value);
|
||||
closeBox();
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_user_privacy.h"
|
||||
|
||||
namespace Ui {
|
||||
class VerticalLayout;
|
||||
@@ -31,15 +31,14 @@ class EditPrivacyBox;
|
||||
|
||||
class EditPrivacyController {
|
||||
public:
|
||||
using Key = ApiWrap::Privacy::Key;
|
||||
using Option = ApiWrap::Privacy::Option;
|
||||
using Key = Api::UserPrivacy::Key;
|
||||
using Option = Api::UserPrivacy::Option;
|
||||
enum class Exception {
|
||||
Always,
|
||||
Never,
|
||||
};
|
||||
|
||||
[[nodiscard]] virtual Key key() = 0;
|
||||
[[nodiscard]] virtual MTPInputPrivacyKey apiKey() = 0;
|
||||
|
||||
[[nodiscard]] virtual rpl::producer<QString> title() = 0;
|
||||
[[nodiscard]] virtual bool hasOption(Option option) {
|
||||
@@ -102,8 +101,8 @@ private:
|
||||
|
||||
class EditPrivacyBox : public Ui::BoxContent {
|
||||
public:
|
||||
using Value = ApiWrap::Privacy;
|
||||
using Option = Value::Option;
|
||||
using Value = Api::UserPrivacy::Rule;
|
||||
using Option = Api::UserPrivacy::Option;
|
||||
using Exception = EditPrivacyController::Exception;
|
||||
|
||||
EditPrivacyBox(
|
||||
@@ -124,7 +123,6 @@ protected:
|
||||
private:
|
||||
bool showExceptionLink(Exception exception) const;
|
||||
void setupContent();
|
||||
QVector<MTPInputPrivacyRule> collectResult();
|
||||
|
||||
Ui::FlatLabel *addLabel(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
|
||||
@@ -11,8 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/confirm_phone_box.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "mainwindow.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_cloud_password.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_domain.h"
|
||||
#include "core/application.h"
|
||||
@@ -42,7 +44,7 @@ enum class PasswordErrorType {
|
||||
void SetCloudPassword(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session) {
|
||||
session->api().passwordState(
|
||||
session->api().cloudPassword().state(
|
||||
) | rpl::start_with_next([=] {
|
||||
using namespace Settings;
|
||||
const auto weak = Ui::MakeWeak(box);
|
||||
@@ -95,6 +97,56 @@ void TransferPasswordError(
|
||||
}
|
||||
}
|
||||
|
||||
void StartPendingReset(
|
||||
not_null<Main::Session*> session,
|
||||
not_null<Ui::BoxContent*> context,
|
||||
Fn<void()> close) {
|
||||
const auto weak = Ui::MakeWeak(context.get());
|
||||
session->api().request(MTPaccount_ResetPassword(
|
||||
)).done([=](const MTPaccount_ResetPasswordResult &result) {
|
||||
session->api().cloudPassword().applyPendingReset(result);
|
||||
result.match([&](const MTPDaccount_resetPasswordOk &data) {
|
||||
}, [&](const MTPDaccount_resetPasswordRequestedWait &data) {
|
||||
}, [&](const MTPDaccount_resetPasswordFailedWait &data) {
|
||||
constexpr auto kMinute = 60;
|
||||
constexpr auto kHour = 3600;
|
||||
constexpr auto kDay = 86400;
|
||||
const auto left = std::max(
|
||||
data.vretry_date().v - base::unixtime::now(),
|
||||
kMinute);
|
||||
const auto days = (left / kDay);
|
||||
const auto hours = (left / kHour);
|
||||
const auto minutes = (left / kMinute);
|
||||
const auto duration = days
|
||||
? tr::lng_group_call_duration_days(tr::now, lt_count, days)
|
||||
: hours
|
||||
? tr::lng_group_call_duration_hours(tr::now, lt_count, hours)
|
||||
: tr::lng_group_call_duration_minutes(
|
||||
tr::now,
|
||||
lt_count,
|
||||
minutes);
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->getDelegate()->show(Box<InformBox>(
|
||||
tr::lng_cloud_password_reset_later(
|
||||
tr::now,
|
||||
lt_duration,
|
||||
duration)));
|
||||
}
|
||||
});
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
close();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->getDelegate()->show(
|
||||
Box<InformBox>("Error: " + error.type()));
|
||||
strong->closeBox();
|
||||
}
|
||||
close();
|
||||
}).send();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PasscodeBox::CloudFields PasscodeBox::CloudFields::From(
|
||||
@@ -106,6 +158,7 @@ PasscodeBox::CloudFields PasscodeBox::CloudFields::From(
|
||||
result.hasRecovery = current.hasRecovery;
|
||||
result.notEmptyPassport = current.notEmptyPassport;
|
||||
result.hint = current.hint;
|
||||
result.pendingResetDate = current.pendingResetDate;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -145,7 +198,8 @@ PasscodeBox::PasscodeBox(
|
||||
, _reenterPasscode(this, st::defaultInputField, tr::lng_cloud_password_confirm_new())
|
||||
, _passwordHint(this, st::defaultInputField, fields.curRequest ? tr::lng_cloud_password_change_hint() : tr::lng_cloud_password_hint())
|
||||
, _recoverEmail(this, st::defaultInputField, tr::lng_cloud_password_email())
|
||||
, _recover(this, tr::lng_signin_recover(tr::now)) {
|
||||
, _recover(this, tr::lng_signin_recover(tr::now))
|
||||
, _showRecoverLink(_cloudFields.hasRecovery || !_cloudFields.pendingResetDate) {
|
||||
Expects(!_turningOff || _cloudFields.curRequest);
|
||||
|
||||
if (!_cloudFields.hint.isEmpty()) {
|
||||
@@ -203,14 +257,14 @@ void PasscodeBox::prepare() {
|
||||
: _cloudPwd
|
||||
? tr::lng_cloud_password_remove()
|
||||
: tr::lng_passcode_remove());
|
||||
setDimensions(st::boxWidth, st::passcodePadding.top() + _oldPasscode->height() + st::passcodeTextLine + ((_cloudFields.hasRecovery && !_hintText.isEmpty()) ? st::passcodeTextLine : 0) + st::passcodeAboutSkip + _aboutHeight + st::passcodePadding.bottom());
|
||||
setDimensions(st::boxWidth, st::passcodePadding.top() + _oldPasscode->height() + st::passcodeTextLine + ((_showRecoverLink && !_hintText.isEmpty()) ? st::passcodeTextLine : 0) + st::passcodeAboutSkip + _aboutHeight + st::passcodePadding.bottom());
|
||||
} else {
|
||||
if (currentlyHave()) {
|
||||
_oldPasscode->show();
|
||||
setTitle(_cloudPwd
|
||||
? tr::lng_cloud_password_change()
|
||||
: tr::lng_passcode_change());
|
||||
setDimensions(st::boxWidth, st::passcodePadding.top() + _oldPasscode->height() + st::passcodeTextLine + ((_cloudFields.hasRecovery && !_hintText.isEmpty()) ? st::passcodeTextLine : 0) + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + st::passcodePadding.bottom());
|
||||
setDimensions(st::boxWidth, st::passcodePadding.top() + _oldPasscode->height() + st::passcodeTextLine + ((_showRecoverLink && !_hintText.isEmpty()) ? st::passcodeTextLine : 0) + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + st::passcodePadding.bottom());
|
||||
} else {
|
||||
_oldPasscode->hide();
|
||||
setTitle(_cloudPwd
|
||||
@@ -237,7 +291,9 @@ void PasscodeBox::prepare() {
|
||||
|
||||
const auto has = currentlyHave();
|
||||
_oldPasscode->setVisible(onlyCheck || has);
|
||||
_recover->setVisible((onlyCheck || has) && _cloudPwd && _cloudFields.hasRecovery);
|
||||
_recover->setVisible((onlyCheck || has)
|
||||
&& _cloudPwd
|
||||
&& _showRecoverLink);
|
||||
_newPasscode->setVisible(!onlyCheck);
|
||||
_reenterPasscode->setVisible(!onlyCheck);
|
||||
_passwordHint->setVisible(!onlyCheck && _cloudPwd);
|
||||
@@ -285,7 +341,7 @@ void PasscodeBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
int32 w = st::boxWidth - st::boxPadding.left() * 1.5;
|
||||
int32 abouty = (_passwordHint->isHidden() ? ((_reenterPasscode->isHidden() ? (_oldPasscode->y() + (_cloudFields.hasRecovery && !_hintText.isEmpty() ? st::passcodeTextLine : 0)) : _reenterPasscode->y()) + st::passcodeSkip) : _passwordHint->y()) + _oldPasscode->height() + st::passcodeLittleSkip + st::passcodeAboutSkip;
|
||||
int32 abouty = (_passwordHint->isHidden() ? ((_reenterPasscode->isHidden() ? (_oldPasscode->y() + (_showRecoverLink && !_hintText.isEmpty() ? st::passcodeTextLine : 0)) : _reenterPasscode->y()) + st::passcodeSkip) : _passwordHint->y()) + _oldPasscode->height() + st::passcodeLittleSkip + st::passcodeAboutSkip;
|
||||
p.setPen(st::boxTextFg);
|
||||
_about.drawLeft(p, st::boxPadding.left(), abouty, w, width());
|
||||
|
||||
@@ -317,7 +373,7 @@ void PasscodeBox::resizeEvent(QResizeEvent *e) {
|
||||
_oldPasscode->resize(w, _oldPasscode->height());
|
||||
_oldPasscode->moveToLeft(st::boxPadding.left(), st::passcodePadding.top());
|
||||
_newPasscode->resize(w, _newPasscode->height());
|
||||
_newPasscode->moveToLeft(st::boxPadding.left(), _oldPasscode->y() + ((_turningOff || has) ? (_oldPasscode->height() + st::passcodeTextLine + ((_cloudFields.hasRecovery && !_hintText.isEmpty()) ? st::passcodeTextLine : 0)) : 0));
|
||||
_newPasscode->moveToLeft(st::boxPadding.left(), _oldPasscode->y() + ((_turningOff || has) ? (_oldPasscode->height() + st::passcodeTextLine + ((_showRecoverLink && !_hintText.isEmpty()) ? st::passcodeTextLine : 0)) : 0));
|
||||
_reenterPasscode->resize(w, _reenterPasscode->height());
|
||||
_reenterPasscode->moveToLeft(st::boxPadding.left(), _newPasscode->y() + _newPasscode->height() + st::passcodeLittleSkip);
|
||||
_passwordHint->resize(w, _passwordHint->height());
|
||||
@@ -379,7 +435,7 @@ void PasscodeBox::setPasswordFail(const QString &type) {
|
||||
_oldPasscode->setFocus();
|
||||
_oldPasscode->showError();
|
||||
_oldError = tr::lng_flood_error(tr::now);
|
||||
if (_cloudFields.hasRecovery && _hintText.isEmpty()) {
|
||||
if (_showRecoverLink && _hintText.isEmpty()) {
|
||||
_recover->hide();
|
||||
}
|
||||
update();
|
||||
@@ -913,7 +969,7 @@ void PasscodeBox::badOldPasscode() {
|
||||
_oldError = _cloudPwd
|
||||
? tr::lng_cloud_password_wrong(tr::now)
|
||||
: tr::lng_passcode_wrong(tr::now);
|
||||
if (_cloudFields.hasRecovery && _hintText.isEmpty()) {
|
||||
if (_showRecoverLink && _hintText.isEmpty()) {
|
||||
_recover->hide();
|
||||
}
|
||||
update();
|
||||
@@ -922,7 +978,7 @@ void PasscodeBox::badOldPasscode() {
|
||||
void PasscodeBox::oldChanged() {
|
||||
if (!_oldError.isEmpty()) {
|
||||
_oldError = QString();
|
||||
if (_cloudFields.hasRecovery && _hintText.isEmpty()) {
|
||||
if (_showRecoverLink && _hintText.isEmpty()) {
|
||||
_recover->show();
|
||||
}
|
||||
update();
|
||||
@@ -944,7 +1000,21 @@ void PasscodeBox::emailChanged() {
|
||||
}
|
||||
|
||||
void PasscodeBox::recoverByEmail() {
|
||||
if (_pattern.isEmpty()) {
|
||||
if (!_cloudFields.hasRecovery) {
|
||||
const auto session = _session;
|
||||
const auto confirmBox = std::make_shared<QPointer<BoxContent>>();
|
||||
const auto reset = crl::guard(this, [=] {
|
||||
StartPendingReset(session, this, [=] {
|
||||
if (const auto box = *confirmBox) {
|
||||
box->closeBox();
|
||||
}
|
||||
});
|
||||
});
|
||||
*confirmBox = getDelegate()->show(Box<ConfirmBox>(
|
||||
tr::lng_cloud_password_reset_no_email(tr::now),
|
||||
tr::lng_cloud_password_reset_ok(tr::now),
|
||||
reset));
|
||||
} else if (_pattern.isEmpty()) {
|
||||
_pattern = "-";
|
||||
_api.request(MTPauth_RequestPasswordRecovery(
|
||||
)).done([=](const MTPauth_PasswordRecovery &result) {
|
||||
@@ -964,10 +1034,13 @@ void PasscodeBox::recoverExpired() {
|
||||
void PasscodeBox::recover() {
|
||||
if (_pattern == "-") return;
|
||||
|
||||
const auto weak = Ui::MakeWeak(this);
|
||||
const auto box = getDelegate()->show(Box<RecoverBox>(
|
||||
_session,
|
||||
_pattern,
|
||||
_cloudFields.notEmptyPassport));
|
||||
_cloudFields.notEmptyPassport,
|
||||
_cloudFields.pendingResetDate != 0,
|
||||
[weak] { if (weak) { weak->closeBox(); } }));
|
||||
|
||||
box->passwordCleared(
|
||||
) | rpl::map_to(
|
||||
@@ -996,11 +1069,37 @@ RecoverBox::RecoverBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
const QString &pattern,
|
||||
bool notEmptyPassport)
|
||||
bool notEmptyPassport,
|
||||
bool hasPendingReset,
|
||||
Fn<void()> closeParent)
|
||||
: _api(&session->mtp())
|
||||
, _pattern(st::normalFont->elided(tr::lng_signin_recover_hint(tr::now, lt_recover_email, pattern), st::boxWidth - st::boxPadding.left() * 1.5))
|
||||
, _notEmptyPassport(notEmptyPassport)
|
||||
, _recoverCode(this, st::defaultInputField, tr::lng_signin_code()) {
|
||||
, _recoverCode(this, st::defaultInputField, tr::lng_signin_code())
|
||||
, _noEmailAccess(this, tr::lng_signin_try_password(tr::now))
|
||||
, _closeParent(std::move(closeParent)) {
|
||||
if (hasPendingReset) {
|
||||
_noEmailAccess.destroy();
|
||||
} else {
|
||||
_noEmailAccess->setClickedCallback([=] {
|
||||
const auto confirmBox = std::make_shared<QPointer<BoxContent>>();
|
||||
const auto reset = crl::guard(this, [=] {
|
||||
const auto closeParent = _closeParent;
|
||||
StartPendingReset(session, this, [=] {
|
||||
if (closeParent) {
|
||||
closeParent();
|
||||
}
|
||||
if (const auto box = *confirmBox) {
|
||||
box->closeBox();
|
||||
}
|
||||
});
|
||||
});
|
||||
*confirmBox = getDelegate()->show(Box<ConfirmBox>(
|
||||
tr::lng_cloud_password_reset_with_email(tr::now),
|
||||
tr::lng_cloud_password_reset_ok(tr::now),
|
||||
reset));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<> RecoverBox::passwordCleared() const {
|
||||
@@ -1017,7 +1116,13 @@ void RecoverBox::prepare() {
|
||||
addButton(tr::lng_passcode_submit(), [=] { submit(); });
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
|
||||
setDimensions(st::boxWidth, st::passcodePadding.top() + st::passcodePadding.bottom() + st::passcodeTextLine + _recoverCode->height() + st::passcodeTextLine);
|
||||
setDimensions(
|
||||
st::boxWidth,
|
||||
(st::passcodePadding.top()
|
||||
+ st::passcodePadding.bottom()
|
||||
+ st::passcodeTextLine
|
||||
+ _recoverCode->height()
|
||||
+ st::passcodeTextLine));
|
||||
|
||||
connect(_recoverCode, &Ui::InputField::changed, [=] { codeChanged(); });
|
||||
connect(_recoverCode, &Ui::InputField::submitted, [=] { submit(); });
|
||||
@@ -1044,6 +1149,9 @@ void RecoverBox::resizeEvent(QResizeEvent *e) {
|
||||
|
||||
_recoverCode->resize(st::boxWidth - st::boxPadding.left() - st::boxPadding.right(), _recoverCode->height());
|
||||
_recoverCode->moveToLeft(st::boxPadding.left(), st::passcodePadding.top() + st::passcodePadding.bottom() + st::passcodeTextLine);
|
||||
if (_noEmailAccess) {
|
||||
_noEmailAccess->moveToLeft(st::boxPadding.left(), _recoverCode->y() + _recoverCode->height() + (st::passcodeTextLine - _noEmailAccess->height()) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
void RecoverBox::setInnerFocus() {
|
||||
@@ -1085,11 +1193,18 @@ void RecoverBox::submit() {
|
||||
}
|
||||
}
|
||||
|
||||
void RecoverBox::codeChanged() {
|
||||
_error = QString();
|
||||
void RecoverBox::setError(const QString &error) {
|
||||
_error = error;
|
||||
if (_noEmailAccess) {
|
||||
_noEmailAccess->setVisible(error.isEmpty());
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void RecoverBox::codeChanged() {
|
||||
setError(QString());
|
||||
}
|
||||
|
||||
void RecoverBox::codeSubmitDone(const MTPauth_Authorization &result) {
|
||||
_submitRequest = 0;
|
||||
|
||||
@@ -1102,8 +1217,7 @@ void RecoverBox::codeSubmitDone(const MTPauth_Authorization &result) {
|
||||
void RecoverBox::codeSubmitFail(const MTP::Error &error) {
|
||||
if (MTP::IsFloodError(error)) {
|
||||
_submitRequest = 0;
|
||||
_error = tr::lng_flood_error(tr::now);
|
||||
update();
|
||||
setError(tr::lng_flood_error(tr::now));
|
||||
_recoverCode->showError();
|
||||
return;
|
||||
}
|
||||
@@ -1121,18 +1235,14 @@ void RecoverBox::codeSubmitFail(const MTP::Error &error) {
|
||||
_recoveryExpired.fire({});
|
||||
closeBox();
|
||||
} else if (err == qstr("CODE_INVALID")) {
|
||||
_error = tr::lng_signin_wrong_code(tr::now);
|
||||
update();
|
||||
setError(tr::lng_signin_wrong_code(tr::now));
|
||||
_recoverCode->selectAll();
|
||||
_recoverCode->setFocus();
|
||||
_recoverCode->showError();
|
||||
} else {
|
||||
if (Logs::DebugEnabled()) { // internal server error
|
||||
_error = err + ": " + error.description();
|
||||
} else {
|
||||
_error = Lang::Hard::ServerError();
|
||||
}
|
||||
update();
|
||||
setError(Logs::DebugEnabled() // internal server error
|
||||
? (err + ": " + error.description())
|
||||
: Lang::Hard::ServerError());
|
||||
_recoverCode->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
QString hint;
|
||||
Core::SecureSecretAlgo newSecureSecretAlgo;
|
||||
bool turningOff = false;
|
||||
TimeId pendingResetDate = 0;
|
||||
|
||||
// Check cloud password for some action.
|
||||
Fn<void(const Core::CloudPasswordResult &)> customCheckCallback;
|
||||
@@ -157,6 +158,7 @@ private:
|
||||
object_ptr<Ui::InputField> _passwordHint;
|
||||
object_ptr<Ui::InputField> _recoverEmail;
|
||||
object_ptr<Ui::LinkButton> _recover;
|
||||
bool _showRecoverLink = false;
|
||||
|
||||
QString _oldError, _newError, _emailError;
|
||||
|
||||
@@ -172,7 +174,9 @@ public:
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
const QString &pattern,
|
||||
bool notEmptyPassport);
|
||||
bool notEmptyPassport,
|
||||
bool hasPendingReset,
|
||||
Fn<void()> closeParent = nullptr);
|
||||
|
||||
rpl::producer<> passwordCleared() const;
|
||||
rpl::producer<> recoveryExpired() const;
|
||||
@@ -192,6 +196,7 @@ private:
|
||||
void codeChanged();
|
||||
void codeSubmitDone(const MTPauth_Authorization &result);
|
||||
void codeSubmitFail(const MTP::Error &error);
|
||||
void setError(const QString &error);
|
||||
|
||||
MTP::Sender _api;
|
||||
mtpRequestId _submitRequest = 0;
|
||||
@@ -200,6 +205,8 @@ private:
|
||||
bool _notEmptyPassport = false;
|
||||
|
||||
object_ptr<Ui::InputField> _recoverCode;
|
||||
object_ptr<Ui::LinkButton> _noEmailAccess;
|
||||
Fn<void()> _closeParent;
|
||||
|
||||
QString _error;
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/text/format_values.h" // Ui::FormatPhone
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "info/profile/info_profile_cover.h"
|
||||
#include "lang/lang_keys.h"
|
||||
@@ -19,7 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/toast/toast.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_info.h"
|
||||
@@ -145,7 +145,7 @@ void Controller::setupCover() {
|
||||
_window,
|
||||
(_phone.isEmpty()
|
||||
? tr::lng_contact_mobile_hidden()
|
||||
: rpl::single(App::formatPhone(_phone)))),
|
||||
: rpl::single(Ui::FormatPhone(_phone)))),
|
||||
style::margins())->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/unixtime.h"
|
||||
#include "base/qt_adapters.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_cloud_password.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -444,7 +445,7 @@ void EditAdminBox::transferOwnership() {
|
||||
? peer()->asChannel()->inputChannel
|
||||
: MTP_inputChannelEmpty();
|
||||
const auto api = &peer()->session().api();
|
||||
api->reloadPasswordState();
|
||||
api->cloudPassword().reload();
|
||||
_checkTransferRequestId = api->request(MTPchannels_EditCreator(
|
||||
channel,
|
||||
MTP_inputUserEmpty(),
|
||||
@@ -495,7 +496,7 @@ void EditAdminBox::transferOwnershipChecked() {
|
||||
}
|
||||
|
||||
void EditAdminBox::requestTransferPassword(not_null<ChannelData*> channel) {
|
||||
peer()->session().api().passwordState(
|
||||
peer()->session().api().cloudPassword().state(
|
||||
) | rpl::take(
|
||||
1
|
||||
) | rpl::start_with_next([=](const Core::CloudPasswordState &state) {
|
||||
|
||||
@@ -173,6 +173,24 @@ callCameraUnmute: CallButton(callMicrophoneUnmute) {
|
||||
}
|
||||
}
|
||||
}
|
||||
callScreencastOn: CallButton(callMicrophoneMute) {
|
||||
button: IconButton(callButton) {
|
||||
icon: icon {{ "calls/calls_present", callIconFg }};
|
||||
iconPosition: point(-1px, 22px);
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: callMuteRipple;
|
||||
}
|
||||
}
|
||||
}
|
||||
callScreencastOff: CallButton(callMicrophoneUnmute) {
|
||||
button: IconButton(callButton) {
|
||||
icon: icon {{ "calls/calls_present", callIconFgActive }};
|
||||
iconPosition: point(-1px, 22px);
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: callIconActiveRipple;
|
||||
}
|
||||
}
|
||||
}
|
||||
callBottomShadowSize: 124px;
|
||||
|
||||
CallMuteButton {
|
||||
@@ -724,6 +742,12 @@ groupCallTitleLabel: FlatLabel(groupCallSubtitleLabel) {
|
||||
linkFontOver: font(semibold 14px);
|
||||
}
|
||||
}
|
||||
groupCallVideoLimitLabel: FlatLabel(defaultFlatLabel) {
|
||||
align: align(top);
|
||||
textFg: groupCallMemberNotJoinedStatus;
|
||||
style: semiboldTextStyle;
|
||||
minWidth: 96px;
|
||||
}
|
||||
groupCallAddButtonPosition: point(10px, 7px);
|
||||
groupCallMembersWidthMax: 480px;
|
||||
groupCallRecordingMark: 6px;
|
||||
@@ -1215,6 +1239,7 @@ groupCallNarrowColoredCrossLine: CrossLineAnimation(groupCallNarrowInactiveCross
|
||||
groupCallNarrowRaisedHand: icon {{ "calls/video_mini_speak", groupCallMemberInactiveStatus }};
|
||||
groupCallNarrowCameraIcon: icon {{ "calls/video_mini_video", groupCallMemberNotJoinedStatus }};
|
||||
groupCallNarrowScreenIcon: icon {{ "calls/video_mini_screencast", groupCallMemberNotJoinedStatus }};
|
||||
groupCallNarrowInvitedIcon: icon {{ "calls/video_mini_invited", groupCallMemberNotJoinedStatus }};
|
||||
groupCallNarrowIconPosition: point(-4px, 2px);
|
||||
groupCallNarrowIconSkip: 15px;
|
||||
groupCallOutline: 2px;
|
||||
|
||||
@@ -398,7 +398,7 @@ void BoxController::receivedCalls(const QVector<MTPMessage> &result) {
|
||||
if (const auto peer = session().data().peerLoaded(peerId)) {
|
||||
const auto item = session().data().addNewMessage(
|
||||
message,
|
||||
MTPDmessage_ClientFlags(),
|
||||
MessageFlags(),
|
||||
NewMessageType::Existing);
|
||||
insertRow(item, InsertWay::Append);
|
||||
} else {
|
||||
|
||||
@@ -290,16 +290,8 @@ void Call::startIncoming() {
|
||||
}).send();
|
||||
}
|
||||
|
||||
void Call::switchVideoOutgoing() {
|
||||
const auto video = _videoOutgoing->state() == Webrtc::VideoState::Active;
|
||||
_delegate->callRequestPermissionsOrFail(crl::guard(this, [=] {
|
||||
videoOutgoing()->setState(StartVideoState(!video));
|
||||
}), true);
|
||||
|
||||
}
|
||||
|
||||
void Call::answer() {
|
||||
const auto video = _videoOutgoing->state() == Webrtc::VideoState::Active;
|
||||
const auto video = isSharingVideo();
|
||||
_delegate->callRequestPermissionsOrFail(crl::guard(this, [=] {
|
||||
actuallyAnswer();
|
||||
}), video);
|
||||
@@ -366,7 +358,9 @@ void Call::setupOutgoingVideo() {
|
||||
}
|
||||
_videoOutgoing->stateValue(
|
||||
) | rpl::start_with_next([=](Webrtc::VideoState state) {
|
||||
if (state != Webrtc::VideoState::Inactive && !hasDevices()) {
|
||||
if (state != Webrtc::VideoState::Inactive
|
||||
&& !hasDevices()
|
||||
&& !_videoCaptureIsScreencast) {
|
||||
_errors.fire({ ErrorType::NoCamera });
|
||||
_videoOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
} else if (_state.current() != State::Established
|
||||
@@ -383,7 +377,9 @@ void Call::setupOutgoingVideo() {
|
||||
// Paused not supported right now.
|
||||
Assert(state == Webrtc::VideoState::Active);
|
||||
if (!_videoCapture) {
|
||||
_videoCapture = _delegate->callGetVideoCapture();
|
||||
_videoCapture = _delegate->callGetVideoCapture(
|
||||
_videoCaptureDeviceId,
|
||||
_videoCaptureIsScreencast);
|
||||
_videoCapture->setOutput(_videoOutgoing->sink());
|
||||
}
|
||||
if (_instance) {
|
||||
@@ -986,9 +982,15 @@ void Call::setCurrentAudioDevice(bool input, const QString &deviceId) {
|
||||
}
|
||||
}
|
||||
|
||||
void Call::setCurrentVideoDevice(const QString &deviceId) {
|
||||
if (_videoCapture) {
|
||||
_videoCapture->switchToDevice(deviceId.toStdString());
|
||||
void Call::setCurrentCameraDevice(const QString &deviceId) {
|
||||
if (!_videoCaptureIsScreencast) {
|
||||
_videoCaptureDeviceId = deviceId;
|
||||
if (_videoCapture) {
|
||||
_videoCapture->switchToDevice(deviceId.toStdString(), false);
|
||||
if (_instance) {
|
||||
_instance->sendVideoDeviceUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1008,6 +1010,77 @@ void Call::setAudioDuckingEnabled(bool enabled) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Call::isSharingVideo() const {
|
||||
return (_videoOutgoing->state() != Webrtc::VideoState::Inactive);
|
||||
}
|
||||
|
||||
bool Call::isSharingCamera() const {
|
||||
return !_videoCaptureIsScreencast && isSharingVideo();
|
||||
}
|
||||
|
||||
bool Call::isSharingScreen() const {
|
||||
return _videoCaptureIsScreencast && isSharingVideo();
|
||||
}
|
||||
|
||||
QString Call::cameraSharingDeviceId() const {
|
||||
return isSharingCamera() ? _videoCaptureDeviceId : QString();
|
||||
}
|
||||
|
||||
QString Call::screenSharingDeviceId() const {
|
||||
return isSharingScreen() ? _videoCaptureDeviceId : QString();
|
||||
}
|
||||
|
||||
void Call::toggleCameraSharing(bool enabled) {
|
||||
if (isSharingCamera() == enabled) {
|
||||
return;
|
||||
} else if (!enabled) {
|
||||
if (_videoCapture) {
|
||||
_videoCapture->setState(tgcalls::VideoState::Inactive);
|
||||
}
|
||||
_videoOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
_videoCaptureDeviceId = QString();
|
||||
return;
|
||||
}
|
||||
_delegate->callRequestPermissionsOrFail(crl::guard(this, [=] {
|
||||
toggleScreenSharing(std::nullopt);
|
||||
const auto deviceId = Core::App().settings().callVideoInputDeviceId();
|
||||
_videoCaptureDeviceId = deviceId;
|
||||
if (_videoCapture) {
|
||||
_videoCapture->switchToDevice(deviceId.toStdString(), false);
|
||||
if (_instance) {
|
||||
_instance->sendVideoDeviceUpdated();
|
||||
}
|
||||
}
|
||||
_videoOutgoing->setState(Webrtc::VideoState::Active);
|
||||
}), true);
|
||||
}
|
||||
|
||||
void Call::toggleScreenSharing(std::optional<QString> uniqueId) {
|
||||
if (!uniqueId) {
|
||||
if (isSharingScreen()) {
|
||||
if (_videoCapture) {
|
||||
_videoCapture->setState(tgcalls::VideoState::Inactive);
|
||||
}
|
||||
_videoOutgoing->setState(Webrtc::VideoState::Inactive);
|
||||
}
|
||||
_videoCaptureDeviceId = QString();
|
||||
_videoCaptureIsScreencast = false;
|
||||
return;
|
||||
} else if (screenSharingDeviceId() == *uniqueId) {
|
||||
return;
|
||||
}
|
||||
toggleCameraSharing(false);
|
||||
_videoCaptureIsScreencast = true;
|
||||
_videoCaptureDeviceId = *uniqueId;
|
||||
if (_videoCapture) {
|
||||
_videoCapture->switchToDevice(uniqueId->toStdString(), true);
|
||||
if (_instance) {
|
||||
_instance->sendVideoDeviceUpdated();
|
||||
}
|
||||
}
|
||||
_videoOutgoing->setState(Webrtc::VideoState::Active);
|
||||
}
|
||||
|
||||
void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
|
||||
Expects(type != FinishType::None);
|
||||
|
||||
|
||||
@@ -77,8 +77,10 @@ public:
|
||||
Fn<void()> onSuccess,
|
||||
bool video) = 0;
|
||||
|
||||
virtual auto callGetVideoCapture()
|
||||
-> std::shared_ptr<tgcalls::VideoCaptureInterface> = 0;
|
||||
virtual auto callGetVideoCapture(
|
||||
const QString &deviceId,
|
||||
bool isScreenCapture)
|
||||
-> std::shared_ptr<tgcalls::VideoCaptureInterface> = 0;
|
||||
|
||||
virtual ~Delegate() = default;
|
||||
|
||||
@@ -174,7 +176,6 @@ public:
|
||||
crl::time getDurationMs() const;
|
||||
float64 getWaitingSoundPeakValue() const;
|
||||
|
||||
void switchVideoOutgoing();
|
||||
void answer();
|
||||
void hangup();
|
||||
void redial();
|
||||
@@ -185,10 +186,22 @@ public:
|
||||
QString getDebugLog() const;
|
||||
|
||||
void setCurrentAudioDevice(bool input, const QString &deviceId);
|
||||
void setCurrentVideoDevice(const QString &deviceId);
|
||||
//void setAudioVolume(bool input, float level);
|
||||
void setAudioDuckingEnabled(bool enabled);
|
||||
|
||||
void setCurrentCameraDevice(const QString &deviceId);
|
||||
[[nodiscard]] QString videoDeviceId() const {
|
||||
return _videoCaptureDeviceId;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isSharingVideo() const;
|
||||
[[nodiscard]] bool isSharingCamera() const;
|
||||
[[nodiscard]] bool isSharingScreen() const;
|
||||
[[nodiscard]] QString cameraSharingDeviceId() const;
|
||||
[[nodiscard]] QString screenSharingDeviceId() const;
|
||||
void toggleCameraSharing(bool enabled);
|
||||
void toggleScreenSharing(std::optional<QString> uniqueId);
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
@@ -268,6 +281,8 @@ private:
|
||||
|
||||
std::unique_ptr<tgcalls::Instance> _instance;
|
||||
std::shared_ptr<tgcalls::VideoCaptureInterface> _videoCapture;
|
||||
QString _videoCaptureDeviceId;
|
||||
bool _videoCaptureIsScreencast = false;
|
||||
const std::unique_ptr<Webrtc::VideoTrack> _videoIncoming;
|
||||
const std::unique_ptr<Webrtc::VideoTrack> _videoOutgoing;
|
||||
|
||||
|
||||
@@ -64,8 +64,10 @@ public:
|
||||
Fn<void()> onSuccess,
|
||||
bool video) override;
|
||||
void callPlaySound(CallSound sound) override;
|
||||
auto callGetVideoCapture()
|
||||
-> std::shared_ptr<tgcalls::VideoCaptureInterface> override;
|
||||
auto callGetVideoCapture(
|
||||
const QString &deviceId,
|
||||
bool isScreenCapture)
|
||||
-> std::shared_ptr<tgcalls::VideoCaptureInterface> override;
|
||||
|
||||
void groupCallFinished(not_null<GroupCall*> call) override;
|
||||
void groupCallFailed(not_null<GroupCall*> call) override;
|
||||
@@ -123,9 +125,11 @@ void Instance::Delegate::callPlaySound(CallSound sound) {
|
||||
}());
|
||||
}
|
||||
|
||||
auto Instance::Delegate::callGetVideoCapture()
|
||||
auto Instance::Delegate::callGetVideoCapture(
|
||||
const QString &deviceId,
|
||||
bool isScreenCapture)
|
||||
-> std::shared_ptr<tgcalls::VideoCaptureInterface> {
|
||||
return _instance->getVideoCapture();
|
||||
return _instance->getVideoCapture(deviceId, isScreenCapture);
|
||||
}
|
||||
|
||||
void Instance::Delegate::groupCallFinished(not_null<GroupCall*> call) {
|
||||
@@ -159,7 +163,7 @@ void Instance::Delegate::groupCallPlaySound(GroupCallSound sound) {
|
||||
|
||||
auto Instance::Delegate::groupCallGetVideoCapture(const QString &deviceId)
|
||||
-> std::shared_ptr<tgcalls::VideoCaptureInterface> {
|
||||
return _instance->getVideoCapture(deviceId);
|
||||
return _instance->getVideoCapture(deviceId, false);
|
||||
}
|
||||
|
||||
FnMut<void()> Instance::Delegate::groupCallAddAsyncWaiter() {
|
||||
@@ -699,18 +703,25 @@ void Instance::requestPermissionOrFail(Platform::PermissionType type, Fn<void()>
|
||||
}
|
||||
|
||||
std::shared_ptr<tgcalls::VideoCaptureInterface> Instance::getVideoCapture(
|
||||
QString deviceId) {
|
||||
if (deviceId.isEmpty()) {
|
||||
deviceId = Core::App().settings().callVideoInputDeviceId();
|
||||
}
|
||||
std::optional<QString> deviceId,
|
||||
bool isScreenCapture) {
|
||||
if (auto result = _videoCapture.lock()) {
|
||||
result->switchToDevice(deviceId.toStdString());
|
||||
if (deviceId) {
|
||||
result->switchToDevice(
|
||||
(deviceId->isEmpty()
|
||||
? Core::App().settings().callVideoInputDeviceId()
|
||||
: *deviceId).toStdString(),
|
||||
isScreenCapture);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const auto startDeviceId = (deviceId && !deviceId->isEmpty())
|
||||
? *deviceId
|
||||
: Core::App().settings().callVideoInputDeviceId();
|
||||
auto result = std::shared_ptr<tgcalls::VideoCaptureInterface>(
|
||||
tgcalls::VideoCaptureInterface::Create(
|
||||
tgcalls::StaticThreads::getThreads(),
|
||||
deviceId.toStdString()));
|
||||
startDeviceId.toStdString()));
|
||||
_videoCapture = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,9 @@ public:
|
||||
bool activateCurrentCall(const QString &joinHash = QString());
|
||||
bool minimizeCurrentActiveCall();
|
||||
bool closeCurrentActiveCall();
|
||||
[[nodiscard]] auto getVideoCapture(QString deviceId = QString())
|
||||
[[nodiscard]] auto getVideoCapture(
|
||||
std::optional<QString> deviceId = std::nullopt,
|
||||
bool isScreenCapture = false)
|
||||
-> std::shared_ptr<tgcalls::VideoCaptureInterface>;
|
||||
void requestPermissionsOrFail(Fn<void()> onSuccess, bool video = true);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_photo_media.h"
|
||||
#include "data/data_cloud_file.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "calls/group/calls_group_common.h"
|
||||
#include "calls/calls_emoji_fingerprint.h"
|
||||
#include "calls/calls_signal_bars.h"
|
||||
#include "calls/calls_userpic.h"
|
||||
@@ -24,7 +25,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/widgets/window.h"
|
||||
#include "ui/widgets/rp_window.h"
|
||||
#include "ui/layers/layer_manager.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
@@ -44,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/main_window.h"
|
||||
#include "app.h"
|
||||
#include "webrtc/webrtc_video_track.h"
|
||||
#include "webrtc/webrtc_media_devices.h"
|
||||
#include "styles/style_calls.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
@@ -57,6 +61,7 @@ namespace Calls {
|
||||
Panel::Panel(not_null<Call*> call)
|
||||
: _call(call)
|
||||
, _user(call->user())
|
||||
, _layerBg(std::make_unique<Ui::LayerManager>(widget()))
|
||||
#ifndef Q_OS_MAC
|
||||
, _controls(std::make_unique<Ui::Platform::TitleControls>(
|
||||
widget(),
|
||||
@@ -67,10 +72,14 @@ Panel::Panel(not_null<Call*> call)
|
||||
, _answerHangupRedial(widget(), st::callAnswer, &st::callHangup)
|
||||
, _decline(widget(), object_ptr<Ui::CallButton>(widget(), st::callHangup))
|
||||
, _cancel(widget(), object_ptr<Ui::CallButton>(widget(), st::callCancel))
|
||||
, _screencast(widget(), st::callScreencastOn, &st::callScreencastOff)
|
||||
, _camera(widget(), st::callCameraMute, &st::callCameraUnmute)
|
||||
, _mute(widget(), st::callMicrophoneMute, &st::callMicrophoneUnmute)
|
||||
, _name(widget(), st::callName)
|
||||
, _status(widget(), st::callStatus) {
|
||||
_layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox);
|
||||
_layerBg->setHideByBackgroundClick(true);
|
||||
|
||||
_decline->setDuration(st::callPanelDuration);
|
||||
_decline->entity()->setText(tr::lng_call_decline());
|
||||
_cancel->setDuration(st::callPanelDuration);
|
||||
@@ -152,9 +161,13 @@ void Panel::initWindow() {
|
||||
_answerHangupRedial->height()).contains(widgetPoint)
|
||||
|| (!_outgoingPreviewInBody
|
||||
&& _outgoingVideoBubble->geometry().contains(widgetPoint));
|
||||
return inControls
|
||||
? Flag::None
|
||||
: (Flag::Move | Flag::FullScreen);
|
||||
if (inControls) {
|
||||
return Flag::None | Flag(0);
|
||||
}
|
||||
const auto shown = _layerBg->topShownLayer();
|
||||
return (!shown || !shown->geometry().contains(widgetPoint))
|
||||
? (Flag::Move | Flag::FullScreen)
|
||||
: Flag::None;
|
||||
});
|
||||
|
||||
// Don't do that, it looks awful :(
|
||||
@@ -205,9 +218,28 @@ void Panel::initControls() {
|
||||
_call->setMuted(!_call->muted());
|
||||
}
|
||||
});
|
||||
_screencast->setClickedCallback([=] {
|
||||
if (!_call) {
|
||||
return;
|
||||
} else if (!Webrtc::DesktopCaptureAllowed()) {
|
||||
if (auto box = Group::ScreenSharingPrivacyRequestBox()) {
|
||||
_layerBg->showBox(std::move(box));
|
||||
}
|
||||
} else if (const auto source = Webrtc::UniqueDesktopCaptureSource()) {
|
||||
if (_call->isSharingScreen()) {
|
||||
_call->toggleScreenSharing(std::nullopt);
|
||||
} else {
|
||||
chooseSourceAccepted(*source, false);
|
||||
}
|
||||
} else {
|
||||
Group::Ui::DesktopCapture::ChooseSource(this);
|
||||
}
|
||||
});
|
||||
_camera->setClickedCallback([=] {
|
||||
if (_call) {
|
||||
_call->switchVideoOutgoing();
|
||||
if (!_call) {
|
||||
return;
|
||||
} else {
|
||||
_call->toggleCameraSharing(!_call->isSharingCamera());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -218,7 +250,8 @@ void Panel::initControls() {
|
||||
});
|
||||
_updateOuterRippleTimer.setCallback([this] {
|
||||
if (_call) {
|
||||
_answerHangupRedial->setOuterValue(_call->getWaitingSoundPeakValue());
|
||||
_answerHangupRedial->setOuterValue(
|
||||
_call->getWaitingSoundPeakValue());
|
||||
} else {
|
||||
_answerHangupRedial->setOuterValue(0.);
|
||||
_updateOuterRippleTimer.cancel();
|
||||
@@ -260,6 +293,40 @@ void Panel::setIncomingSize(QSize size) {
|
||||
showControls();
|
||||
}
|
||||
|
||||
QWidget *Panel::chooseSourceParent() {
|
||||
return window().get();
|
||||
}
|
||||
|
||||
QString Panel::chooseSourceActiveDeviceId() {
|
||||
return _call->screenSharingDeviceId();
|
||||
}
|
||||
|
||||
bool Panel::chooseSourceActiveWithAudio() {
|
||||
return false;// _call->screenSharingWithAudio();
|
||||
}
|
||||
|
||||
bool Panel::chooseSourceWithAudioSupported() {
|
||||
//#ifdef Q_OS_WIN
|
||||
// return true;
|
||||
//#else // Q_OS_WIN
|
||||
return false;
|
||||
//#endif // Q_OS_WIN
|
||||
}
|
||||
|
||||
rpl::lifetime &Panel::chooseSourceInstanceLifetime() {
|
||||
return lifetime();
|
||||
}
|
||||
|
||||
void Panel::chooseSourceAccepted(
|
||||
const QString &deviceId,
|
||||
bool withAudio) {
|
||||
_call->toggleScreenSharing(deviceId/*, withAudio*/);
|
||||
}
|
||||
|
||||
void Panel::chooseSourceStop() {
|
||||
_call->toggleScreenSharing(std::nullopt);
|
||||
}
|
||||
|
||||
void Panel::refreshIncomingGeometry() {
|
||||
Expects(_call != nullptr);
|
||||
Expects(_incoming != nullptr);
|
||||
@@ -332,12 +399,19 @@ void Panel::reinitWithCall(Call *call) {
|
||||
}, _callLifetime);
|
||||
|
||||
_call->videoOutgoing()->stateValue(
|
||||
) | rpl::start_with_next([=](Webrtc::VideoState state) {
|
||||
const auto active = (state == Webrtc::VideoState::Active);
|
||||
_camera->setProgress(active ? 0. : 1.);
|
||||
_camera->setText(active
|
||||
? tr::lng_call_stop_video()
|
||||
: tr::lng_call_start_video());
|
||||
) | rpl::start_with_next([=] {
|
||||
{
|
||||
const auto active = _call->isSharingCamera();
|
||||
_camera->setProgress(active ? 0. : 1.);
|
||||
_camera->setText(active
|
||||
? tr::lng_call_stop_video()
|
||||
: tr::lng_call_start_video());
|
||||
}
|
||||
{
|
||||
const auto active = _call->isSharingScreen();
|
||||
_screencast->setProgress(active ? 0. : 1.);
|
||||
_screencast->setText(tr::lng_call_screencast());
|
||||
}
|
||||
}, _callLifetime);
|
||||
|
||||
_call->stateValue(
|
||||
@@ -646,9 +720,11 @@ void Panel::updateControlsGeometry() {
|
||||
updateOutgoingVideoBubbleGeometry();
|
||||
}
|
||||
|
||||
auto bothWidth = _answerHangupRedial->width() + st::callCancel.button.width;
|
||||
_decline->moveToLeft((widget()->width() - bothWidth) / 2, _buttonsTop);
|
||||
_cancel->moveToLeft((widget()->width() - bothWidth) / 2, _buttonsTop);
|
||||
auto threeWidth = _answerHangupRedial->width()
|
||||
+ st::callCancel.button.width
|
||||
- _screencast->width();
|
||||
_decline->moveToLeft((widget()->width() - threeWidth) / 2, _buttonsTop);
|
||||
_cancel->moveToLeft((widget()->width() - threeWidth) / 2, _buttonsTop);
|
||||
|
||||
updateHangupGeometry();
|
||||
}
|
||||
@@ -670,16 +746,19 @@ void Panel::updateOutgoingVideoBubbleGeometry() {
|
||||
}
|
||||
|
||||
void Panel::updateHangupGeometry() {
|
||||
auto singleWidth = _answerHangupRedial->width();
|
||||
auto bothWidth = singleWidth + st::callCancel.button.width;
|
||||
auto rightFrom = (widget()->width() - bothWidth) / 2;
|
||||
auto rightTo = (widget()->width() - singleWidth) / 2;
|
||||
auto twoWidth = _answerHangupRedial->width() + _screencast->width();
|
||||
auto threeWidth = twoWidth + st::callCancel.button.width;
|
||||
auto rightFrom = (widget()->width() - threeWidth) / 2;
|
||||
auto rightTo = (widget()->width() - twoWidth) / 2;
|
||||
auto hangupProgress = _hangupShownProgress.value(_hangupShown ? 1. : 0.);
|
||||
auto hangupRight = anim::interpolate(rightFrom, rightTo, hangupProgress);
|
||||
_answerHangupRedial->moveToRight(hangupRight, _buttonsTop);
|
||||
_answerHangupRedial->setProgress(hangupProgress);
|
||||
_mute->moveToRight(hangupRight - _mute->width(), _buttonsTop);
|
||||
_camera->moveToLeft(hangupRight - _mute->width(), _buttonsTop);
|
||||
_screencast->moveToLeft(hangupRight - _mute->width(), _buttonsTop);
|
||||
_camera->moveToLeft(
|
||||
hangupRight - _mute->width() + _screencast->width(),
|
||||
_buttonsTop);
|
||||
}
|
||||
|
||||
void Panel::updateStatusGeometry() {
|
||||
@@ -709,7 +788,7 @@ void Panel::handleClose() {
|
||||
}
|
||||
}
|
||||
|
||||
not_null<Ui::Window*> Panel::window() const {
|
||||
not_null<Ui::RpWindow*> Panel::window() const {
|
||||
return _window.window();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/timer.h"
|
||||
#include "base/object_ptr.h"
|
||||
#include "calls/calls_call.h"
|
||||
#include "calls/group/ui/desktop_capture_choose_source.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/gl/gl_window.h"
|
||||
#include "ui/rp_widget.h"
|
||||
@@ -25,12 +26,13 @@ class CloudImageView;
|
||||
namespace Ui {
|
||||
class IconButton;
|
||||
class CallButton;
|
||||
class LayerManager;
|
||||
class FlatLabel;
|
||||
template <typename Widget>
|
||||
class FadeWrap;
|
||||
template <typename Widget>
|
||||
class PaddingWrap;
|
||||
class Window;
|
||||
class RpWindow;
|
||||
namespace GL {
|
||||
enum class Backend;
|
||||
} // namespace GL
|
||||
@@ -50,7 +52,7 @@ class Userpic;
|
||||
class SignalBars;
|
||||
class VideoBubble;
|
||||
|
||||
class Panel final {
|
||||
class Panel final : private Group::Ui::DesktopCapture::ChooseSourceDelegate {
|
||||
public:
|
||||
Panel(not_null<Call*> call);
|
||||
~Panel();
|
||||
@@ -61,7 +63,17 @@ public:
|
||||
void replaceCall(not_null<Call*> call);
|
||||
void closeBeforeDestroy();
|
||||
|
||||
rpl::lifetime &lifetime();
|
||||
QWidget *chooseSourceParent() override;
|
||||
QString chooseSourceActiveDeviceId() override;
|
||||
bool chooseSourceActiveWithAudio() override;
|
||||
bool chooseSourceWithAudioSupported() override;
|
||||
rpl::lifetime &chooseSourceInstanceLifetime() override;
|
||||
void chooseSourceAccepted(
|
||||
const QString &deviceId,
|
||||
bool withAudio) override;
|
||||
void chooseSourceStop() override;
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime();
|
||||
|
||||
private:
|
||||
class Incoming;
|
||||
@@ -73,7 +85,7 @@ private:
|
||||
Redial,
|
||||
};
|
||||
|
||||
[[nodiscard]] not_null<Ui::Window*> window() const;
|
||||
[[nodiscard]] not_null<Ui::RpWindow*> window() const;
|
||||
[[nodiscard]] not_null<Ui::RpWidget*> widget() const;
|
||||
|
||||
void paint(QRect clip);
|
||||
@@ -110,6 +122,7 @@ private:
|
||||
not_null<UserData*> _user;
|
||||
|
||||
Ui::GL::Window _window;
|
||||
const std::unique_ptr<Ui::LayerManager> _layerBg;
|
||||
std::unique_ptr<Incoming> _incoming;
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
@@ -128,6 +141,7 @@ private:
|
||||
bool _outgoingPreviewInBody = false;
|
||||
std::optional<AnswerHangupRedialState> _answerHangupRedialState;
|
||||
Ui::Animations::Simple _hangupShownProgress;
|
||||
object_ptr<Ui::CallButton> _screencast;
|
||||
object_ptr<Ui::CallButton> _camera;
|
||||
object_ptr<Ui::CallButton> _mute;
|
||||
object_ptr<Ui::FlatLabel> _name;
|
||||
|
||||
@@ -654,7 +654,7 @@ void GroupCall::toggleScreenSharing(
|
||||
_screenWithAudio = withAudio;
|
||||
_screenState = Webrtc::VideoState::Active;
|
||||
if (changed && wasSharing && isSharingScreen()) {
|
||||
_screenCapture->switchToDevice(uniqueId->toStdString());
|
||||
_screenCapture->switchToDevice(uniqueId->toStdString(), true);
|
||||
}
|
||||
if (_screenInstance) {
|
||||
_screenInstance->setIsMuted(!withAudio);
|
||||
@@ -1951,7 +1951,7 @@ void GroupCall::setupMediaDevices() {
|
||||
) | rpl::start_with_next([=](QString id) {
|
||||
_cameraInputId = id;
|
||||
if (_cameraCapture) {
|
||||
_cameraCapture->switchToDevice(id.toStdString());
|
||||
_cameraCapture->switchToDevice(id.toStdString(), false);
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
@@ -2064,7 +2064,9 @@ void GroupCall::setupOutgoingVideo() {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
_cameraCapture->switchToDevice(_cameraInputId.toStdString());
|
||||
_cameraCapture->switchToDevice(
|
||||
_cameraInputId.toStdString(),
|
||||
false);
|
||||
}
|
||||
if (_instance) {
|
||||
_instance->setVideoCapture(_cameraCapture);
|
||||
@@ -2131,7 +2133,8 @@ void GroupCall::setupOutgoingVideo() {
|
||||
});
|
||||
} else {
|
||||
_screenCapture->switchToDevice(
|
||||
_screenDeviceId.toStdString());
|
||||
_screenDeviceId.toStdString(),
|
||||
true);
|
||||
}
|
||||
if (_screenInstance) {
|
||||
_screenInstance->setVideoCapture(_screenCapture);
|
||||
@@ -3049,10 +3052,6 @@ void GroupCall::setCurrentAudioDevice(bool input, const QString &deviceId) {
|
||||
}
|
||||
}
|
||||
|
||||
void GroupCall::setCurrentVideoDevice(const QString &deviceId) {
|
||||
_mediaDevices->switchToVideoInput(deviceId);
|
||||
}
|
||||
|
||||
void GroupCall::toggleMute(const Group::MuteRequest &data) {
|
||||
if (data.locallyOnly) {
|
||||
applyParticipantLocally(data.peer, data.mute, std::nullopt);
|
||||
|
||||
@@ -370,7 +370,6 @@ public:
|
||||
}
|
||||
|
||||
void setCurrentAudioDevice(bool input, const QString &deviceId);
|
||||
void setCurrentVideoDevice(const QString &deviceId);
|
||||
[[nodiscard]] bool isSharingScreen() const;
|
||||
[[nodiscard]] rpl::producer<bool> isSharingScreenValue() const;
|
||||
[[nodiscard]] bool isScreenPaused() const;
|
||||
|
||||
53
Telegram/SourceFiles/calls/group/calls_group_common.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "calls/group/calls_group_common.h"
|
||||
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_calls.h"
|
||||
|
||||
namespace Calls::Group {
|
||||
|
||||
object_ptr<Ui::GenericBox> ScreenSharingPrivacyRequestBox() {
|
||||
#ifdef Q_OS_MAC
|
||||
if (!Platform::IsMac10_15OrGreater()) {
|
||||
return { nullptr };
|
||||
}
|
||||
return Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box.get(),
|
||||
rpl::combine(
|
||||
tr::lng_group_call_mac_screencast_access(),
|
||||
tr::lng_group_call_mac_recording()
|
||||
) | rpl::map([](QString a, QString b) {
|
||||
auto result = Ui::Text::RichLangValue(a);
|
||||
result.append("\n\n").append(Ui::Text::RichLangValue(b));
|
||||
return result;
|
||||
}),
|
||||
st::groupCallBoxLabel),
|
||||
style::margins(
|
||||
st::boxRowPadding.left(),
|
||||
st::boxPadding.top(),
|
||||
st::boxRowPadding.right(),
|
||||
st::boxPadding.bottom()));
|
||||
box->addButton(tr::lng_group_call_mac_settings(), [=] {
|
||||
Platform::OpenDesktopCapturePrivacySettings();
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
});
|
||||
#else // Q_OS_MAC
|
||||
return { nullptr };
|
||||
#endif // Q_OS_MAC
|
||||
}
|
||||
|
||||
} // namespace Calls::Group
|
||||
@@ -7,8 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/object_ptr.h"
|
||||
|
||||
class UserData;
|
||||
|
||||
namespace Ui {
|
||||
class GenericBox;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Calls::Group {
|
||||
|
||||
constexpr auto kDefaultVolume = 10000;
|
||||
@@ -78,4 +84,6 @@ constexpr inline bool is_flag_type(StickedTooltip) {
|
||||
}
|
||||
using StickedTooltips = base::flags<StickedTooltip>;
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::GenericBox> ScreenSharingPrivacyRequestBox();
|
||||
|
||||
} // namespace Calls::Group
|
||||
|
||||
@@ -46,9 +46,16 @@ constexpr auto kUserpicBlurRadius = 8;
|
||||
|
||||
using Row = MembersRow;
|
||||
|
||||
[[nodiscard]] int VideoParticipantsLimit(not_null<Main::Session*> session) {
|
||||
return int(session->account().appConfig().get<double>(
|
||||
"groupcall_video_participants_max",
|
||||
30.));
|
||||
}
|
||||
|
||||
void SetupVideoPlaceholder(
|
||||
not_null<Ui::RpWidget*> widget,
|
||||
not_null<PeerData*> chat) {
|
||||
not_null<PeerData*> chat,
|
||||
int limit) {
|
||||
struct State {
|
||||
QImage blurred;
|
||||
QImage rounded;
|
||||
@@ -128,9 +135,6 @@ void SetupVideoPlaceholder(
|
||||
size.width());
|
||||
|
||||
const auto skip = st::groupCallVideoLargeSkip;
|
||||
const auto limit = chat->session().account().appConfig().get<double>(
|
||||
"groupcall_video_participants_max",
|
||||
30.);
|
||||
p.setPen(st::groupCallVideoTextFg);
|
||||
const auto text = QRect(
|
||||
skip,
|
||||
@@ -145,6 +149,22 @@ void SetupVideoPlaceholder(
|
||||
}, widget->lifetime());
|
||||
}
|
||||
|
||||
void SetupVideoAboutLimit(
|
||||
not_null<Ui::RpWidget*> widget,
|
||||
not_null<Main::Session*> session,
|
||||
int limit) {
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
widget.get(),
|
||||
tr::lng_group_call_over_limit(lt_count, rpl::single(limit * 1.)),
|
||||
st::groupCallVideoLimitLabel);
|
||||
widget->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
label->resizeToWidth(width);
|
||||
label->moveToLeft(0, st::normalFont->height / 3);
|
||||
widget->resize(width, label->height() + st::normalFont->height);
|
||||
}, label->lifetime());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class Members::Controller final
|
||||
@@ -1036,12 +1056,16 @@ void Members::Controller::rowPaintIcon(
|
||||
return;
|
||||
}
|
||||
const auto narrow = (state.style == MembersRowStyle::Narrow);
|
||||
if (!narrow && state.invited) {
|
||||
st::groupCallMemberInvited.paintInCenter(
|
||||
p,
|
||||
QRect(
|
||||
rect.topLeft() + st::groupCallMemberInvitedPosition,
|
||||
st::groupCallMemberInvited.size()));
|
||||
if (state.invited) {
|
||||
if (narrow) {
|
||||
st::groupCallNarrowInvitedIcon.paintInCenter(p, rect);
|
||||
} else {
|
||||
st::groupCallMemberInvited.paintInCenter(
|
||||
p,
|
||||
QRect(
|
||||
rect.topLeft() + st::groupCallMemberInvitedPosition,
|
||||
st::groupCallMemberInvited.size()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const auto video = (state.style == MembersRowStyle::Video);
|
||||
@@ -1601,6 +1625,7 @@ Members::Members(
|
||||
object_ptr<Ui::VerticalLayout>(_scroll.data())))
|
||||
, _videoWrap(_layout->add(object_ptr<Ui::RpWidget>(_layout.get())))
|
||||
, _videoPlaceholder(std::make_unique<Ui::RpWidget>(_videoWrap.get()))
|
||||
, _videoAboutLimit(std::make_unique<Ui::RpWidget>(_videoWrap.get()))
|
||||
, _viewport(
|
||||
std::make_unique<Viewport>(
|
||||
_videoWrap.get(),
|
||||
@@ -1838,6 +1863,7 @@ void Members::trackViewportGeometry() {
|
||||
_scroll->scrollTopValue(
|
||||
) | rpl::skip(1) | rpl::start_with_next(move, _viewport->lifetime());
|
||||
|
||||
const auto videoLimit = VideoParticipantsLimit(&_call->peer()->session());
|
||||
rpl::combine(
|
||||
_layout->widthValue(),
|
||||
_call->hasNotShownVideoValue()
|
||||
@@ -1846,15 +1872,52 @@ void Members::trackViewportGeometry() {
|
||||
_videoPlaceholder->setGeometry(0, 0, width, height);
|
||||
}, _videoPlaceholder->lifetime());
|
||||
|
||||
SetupVideoPlaceholder(_videoPlaceholder.get(), _call->peer());
|
||||
SetupVideoPlaceholder(_videoPlaceholder.get(), _call->peer(), videoLimit);
|
||||
|
||||
_layout->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
_videoAboutLimit->resizeToWidth(width);
|
||||
}, _videoAboutLimit->lifetime());
|
||||
|
||||
using namespace rpl::mappers;
|
||||
auto aboutLimitRelevant = fullCountValue(
|
||||
) | rpl::map(
|
||||
_1 > videoLimit
|
||||
) | rpl::distinct_until_changed();
|
||||
auto aboutLimitShown = rpl::combine(
|
||||
std::move(aboutLimitRelevant),
|
||||
_call->canManageValue(),
|
||||
_1 && _2);
|
||||
|
||||
SetupVideoAboutLimit(
|
||||
_videoAboutLimit.get(),
|
||||
&_call->peer()->session(),
|
||||
videoLimit);
|
||||
|
||||
rpl::combine(
|
||||
_videoPlaceholder->heightValue(),
|
||||
_viewport->fullHeightValue()
|
||||
) | rpl::start_with_next([=](int placeholder, int viewport) {
|
||||
_viewport->fullHeightValue(),
|
||||
_videoAboutLimit->heightValue(),
|
||||
std::move(aboutLimitShown)
|
||||
) | rpl::start_with_next([=](
|
||||
int placeholder,
|
||||
int viewport,
|
||||
int aboutLimit,
|
||||
bool aboutLimitShown) {
|
||||
if (placeholder > 0 || viewport <= 0 || !aboutLimitShown) {
|
||||
aboutLimitShown = false;
|
||||
}
|
||||
|
||||
// This call may update _videoAboutLimit->height() :(
|
||||
_videoAboutLimit->setVisible(aboutLimitShown);
|
||||
|
||||
_videoAboutLimit->move(0, viewport);
|
||||
_videoWrap->resize(
|
||||
_videoWrap->width(),
|
||||
std::max(placeholder, viewport));
|
||||
std::max(
|
||||
placeholder,
|
||||
(viewport
|
||||
+ (aboutLimitShown ? _videoAboutLimit->height() : 0))));
|
||||
if (viewport > 0) {
|
||||
move();
|
||||
resize();
|
||||
|
||||
@@ -102,6 +102,7 @@ private:
|
||||
not_null<Ui::VerticalLayout*> _layout;
|
||||
const not_null<Ui::RpWidget*> _videoWrap;
|
||||
const std::unique_ptr<Ui::RpWidget> _videoPlaceholder;
|
||||
const std::unique_ptr<Ui::RpWidget> _videoAboutLimit;
|
||||
std::unique_ptr<Viewport> _viewport;
|
||||
rpl::variable<Ui::RpWidget*> _addMemberButton = nullptr;
|
||||
RpWidget *_topSkip = nullptr;
|
||||
|
||||
@@ -25,7 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/dropdown_menu.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/widgets/window.h"
|
||||
#include "ui/widgets/rp_window.h"
|
||||
#include "ui/chat/group_call_bar.h"
|
||||
#include "ui/layers/layer_manager.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
@@ -70,10 +70,6 @@ constexpr auto kRecordingOpacity = 0.6;
|
||||
constexpr auto kStartNoConfirmation = TimeId(10);
|
||||
constexpr auto kControlsBackgroundOpacity = 0.8;
|
||||
constexpr auto kOverrideActiveColorBgAlpha = 172;
|
||||
constexpr auto kMicrophoneTooltipAfterLoudCount = 3;
|
||||
constexpr auto kDropLoudAfterQuietCount = 5;
|
||||
constexpr auto kMicrophoneTooltipLevelThreshold = 0.2;
|
||||
constexpr auto kMicrophoneTooltipCheckInterval = crl::time(500);
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -87,49 +83,6 @@ struct Panel::ControlsBackgroundNarrow {
|
||||
Ui::RpWidget blocker;
|
||||
};
|
||||
|
||||
class Panel::MicLevelTester final {
|
||||
public:
|
||||
explicit MicLevelTester(Fn<void()> show);
|
||||
|
||||
[[nodiscard]] bool showTooltip() const;
|
||||
|
||||
private:
|
||||
void check();
|
||||
|
||||
Fn<void()> _show;
|
||||
base::Timer _timer;
|
||||
Webrtc::AudioInputTester _tester;
|
||||
int _loudCount = 0;
|
||||
int _quietCount = 0;
|
||||
|
||||
};
|
||||
|
||||
Panel::MicLevelTester::MicLevelTester(Fn<void()> show)
|
||||
: _show(std::move(show))
|
||||
, _timer([=] { check(); })
|
||||
, _tester(
|
||||
Core::App().settings().callAudioBackend(),
|
||||
Core::App().settings().callInputDeviceId()) {
|
||||
_timer.callEach(kMicrophoneTooltipCheckInterval);
|
||||
}
|
||||
|
||||
bool Panel::MicLevelTester::showTooltip() const {
|
||||
return (_loudCount >= kMicrophoneTooltipAfterLoudCount);
|
||||
}
|
||||
|
||||
void Panel::MicLevelTester::check() {
|
||||
const auto level = _tester.getAndResetLevel();
|
||||
if (level >= kMicrophoneTooltipLevelThreshold) {
|
||||
_quietCount = 0;
|
||||
if (++_loudCount >= kMicrophoneTooltipAfterLoudCount) {
|
||||
_show();
|
||||
}
|
||||
} else if (_loudCount > 0 && ++_quietCount >= kDropLoudAfterQuietCount) {
|
||||
_quietCount = 0;
|
||||
_loudCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Panel::Panel(not_null<GroupCall*> call)
|
||||
: _call(call)
|
||||
, _peer(call->peer())
|
||||
@@ -315,11 +268,16 @@ void Panel::initWindow() {
|
||||
0,
|
||||
widget()->width(),
|
||||
st::groupCallMembersTop);
|
||||
return (titleRect.contains(widgetPoint)
|
||||
const auto moveable = (titleRect.contains(widgetPoint)
|
||||
&& (!_menuToggle || !_menuToggle->geometry().contains(widgetPoint))
|
||||
&& (!_menu || !_menu->geometry().contains(widgetPoint))
|
||||
&& (!_recordingMark || !_recordingMark->geometry().contains(widgetPoint))
|
||||
&& (!_joinAsToggle || !_joinAsToggle->geometry().contains(widgetPoint)))
|
||||
&& (!_joinAsToggle || !_joinAsToggle->geometry().contains(widgetPoint)));
|
||||
if (!moveable) {
|
||||
return (Flag::None | Flag(0));
|
||||
}
|
||||
const auto shown = _layerBg->topShownLayer();
|
||||
return (!shown || !shown->geometry().contains(widgetPoint))
|
||||
? (Flag::Move | Flag::Maximize)
|
||||
: Flag::None;
|
||||
});
|
||||
@@ -1155,35 +1113,9 @@ void Panel::refreshTopButton() {
|
||||
}
|
||||
|
||||
void Panel::screenSharingPrivacyRequest() {
|
||||
#ifdef Q_OS_MAC
|
||||
if (!Platform::IsMac10_15OrGreater()) {
|
||||
return;
|
||||
if (auto box = ScreenSharingPrivacyRequestBox()) {
|
||||
_layerBg->showBox(std::move(box));
|
||||
}
|
||||
const auto requestInputMonitoring = Platform::IsMac10_15OrGreater();
|
||||
_layerBg->showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box.get(),
|
||||
rpl::combine(
|
||||
tr::lng_group_call_mac_screencast_access(),
|
||||
tr::lng_group_call_mac_recording()
|
||||
) | rpl::map([](QString a, QString b) {
|
||||
auto result = Ui::Text::RichLangValue(a);
|
||||
result.append("\n\n").append(Ui::Text::RichLangValue(b));
|
||||
return result;
|
||||
}),
|
||||
st::groupCallBoxLabel),
|
||||
style::margins(
|
||||
st::boxRowPadding.left(),
|
||||
st::boxPadding.top(),
|
||||
st::boxRowPadding.right(),
|
||||
st::boxPadding.bottom()));
|
||||
box->addButton(tr::lng_group_call_mac_settings(), [=] {
|
||||
Platform::OpenDesktopCapturePrivacySettings();
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}));
|
||||
#endif // Q_OS_MAC
|
||||
}
|
||||
|
||||
void Panel::chooseShareScreenSource() {
|
||||
@@ -2221,7 +2153,7 @@ bool Panel::handleClose() {
|
||||
return false;
|
||||
}
|
||||
|
||||
not_null<Ui::Window*> Panel::window() const {
|
||||
not_null<Ui::RpWindow*> Panel::window() const {
|
||||
return _window.window();
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ class Members;
|
||||
class Viewport;
|
||||
enum class PanelMode;
|
||||
enum class StickedTooltip;
|
||||
class MicLevelTester;
|
||||
|
||||
class Panel final : private Ui::DesktopCapture::ChooseSourceDelegate {
|
||||
public:
|
||||
@@ -94,9 +95,8 @@ private:
|
||||
Activated,
|
||||
Discarded,
|
||||
};
|
||||
class MicLevelTester;
|
||||
|
||||
[[nodiscard]] not_null<Ui::Window*> window() const;
|
||||
[[nodiscard]] not_null<Ui::RpWindow*> window() const;
|
||||
[[nodiscard]] not_null<Ui::RpWidget*> widget() const;
|
||||
|
||||
[[nodiscard]] PanelMode mode() const;
|
||||
|
||||
@@ -53,6 +53,10 @@ namespace Calls::Group {
|
||||
namespace {
|
||||
|
||||
constexpr auto kDelaysCount = 201;
|
||||
constexpr auto kMicrophoneTooltipAfterLoudCount = 3;
|
||||
constexpr auto kDropLoudAfterQuietCount = 5;
|
||||
constexpr auto kMicrophoneTooltipLevelThreshold = 0.2;
|
||||
constexpr auto kMicrophoneTooltipCheckInterval = crl::time(500);
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
constexpr auto kCheckAccessibilityInterval = crl::time(500);
|
||||
@@ -735,4 +739,31 @@ std::pair<Fn<void()>, rpl::lifetime> ShareInviteLinkAction(
|
||||
return { std::move(callback), std::move(lifetime) };
|
||||
}
|
||||
|
||||
MicLevelTester::MicLevelTester(Fn<void()> show)
|
||||
: _show(std::move(show))
|
||||
, _timer([=] { check(); })
|
||||
, _tester(
|
||||
std::make_unique<Webrtc::AudioInputTester>(
|
||||
Core::App().settings().callAudioBackend(),
|
||||
Core::App().settings().callInputDeviceId())) {
|
||||
_timer.callEach(kMicrophoneTooltipCheckInterval);
|
||||
}
|
||||
|
||||
bool MicLevelTester::showTooltip() const {
|
||||
return (_loudCount >= kMicrophoneTooltipAfterLoudCount);
|
||||
}
|
||||
|
||||
void MicLevelTester::check() {
|
||||
const auto level = _tester->getAndResetLevel();
|
||||
if (level >= kMicrophoneTooltipLevelThreshold) {
|
||||
_quietCount = 0;
|
||||
if (++_loudCount >= kMicrophoneTooltipAfterLoudCount) {
|
||||
_show();
|
||||
}
|
||||
} else if (_loudCount > 0 && ++_quietCount >= kDropLoudAfterQuietCount) {
|
||||
_quietCount = 0;
|
||||
_loudCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Calls::Group
|
||||
|
||||
@@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "ui/layers/generic_box.h"
|
||||
|
||||
namespace Webrtc {
|
||||
class AudioInputTester;
|
||||
} // namespace Webrtc
|
||||
|
||||
namespace Calls {
|
||||
class GroupCall;
|
||||
} // namespace Calls
|
||||
@@ -24,4 +28,21 @@ void SettingsBox(
|
||||
Fn<void(object_ptr<Ui::BoxContent>)> showBox,
|
||||
Fn<void(QString)> showToast);
|
||||
|
||||
class MicLevelTester final {
|
||||
public:
|
||||
explicit MicLevelTester(Fn<void()> show);
|
||||
|
||||
[[nodiscard]] bool showTooltip() const;
|
||||
|
||||
private:
|
||||
void check();
|
||||
|
||||
Fn<void()> _show;
|
||||
base::Timer _timer;
|
||||
std::unique_ptr<Webrtc::AudioInputTester> _tester;
|
||||
int _loudCount = 0;
|
||||
int _quietCount = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Calls::Group
|
||||
|
||||
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "calls/group/ui/desktop_capture_choose_source.h"
|
||||
|
||||
#include "ui/widgets/window.h"
|
||||
#include "ui/widgets/rp_window.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@@ -104,7 +104,7 @@ private:
|
||||
std::unique_ptr<ChooseSourceProcess>> &Map();
|
||||
|
||||
const not_null<ChooseSourceDelegate*> _delegate;
|
||||
const std::unique_ptr<Ui::Window> _window;
|
||||
const std::unique_ptr<RpWindow> _window;
|
||||
const std::unique_ptr<ScrollArea> _scroll;
|
||||
const not_null<RpWidget*> _inner;
|
||||
const not_null<RpWidget*> _bottom;
|
||||
@@ -250,7 +250,7 @@ rpl::lifetime &Source::lifetime() {
|
||||
ChooseSourceProcess::ChooseSourceProcess(
|
||||
not_null<ChooseSourceDelegate*> delegate)
|
||||
: _delegate(delegate)
|
||||
, _window(std::make_unique<Ui::Window>())
|
||||
, _window(std::make_unique<RpWindow>())
|
||||
, _scroll(std::make_unique<ScrollArea>(_window->body()))
|
||||
, _inner(_scroll->setOwnedWidget(object_ptr<RpWidget>(_scroll.get())))
|
||||
, _bottom(CreateChild<RpWidget>(_window->body().get()))
|
||||
@@ -423,7 +423,7 @@ void ChooseSourceProcess::setupPanel() {
|
||||
+ rows * st::desktopCaptureSourceSize.height()
|
||||
+ (rows - 1) * skips.height()
|
||||
+ margins.bottom();
|
||||
_inner->resize(width, std::max(height, innerHeight));
|
||||
_inner->resize(width, innerHeight);
|
||||
}, _inner->lifetime());
|
||||
|
||||
if (const auto parent = _delegate->chooseSourceParent()) {
|
||||
|
||||
49
Telegram/SourceFiles/chat_helpers/bot_command.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "chat_helpers/bot_command.h"
|
||||
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history_item.h"
|
||||
|
||||
namespace Bot {
|
||||
|
||||
QString WrapCommandInChat(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &command,
|
||||
const FullMsgId &context) {
|
||||
auto result = command;
|
||||
if (const auto item = peer->owner().message(context)) {
|
||||
if (const auto user = item->fromOriginal()->asUser()) {
|
||||
return WrapCommandInChat(peer, command, user);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString WrapCommandInChat(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &command,
|
||||
not_null<UserData*> bot) {
|
||||
if (!bot->isBot() || bot->username.isEmpty()) {
|
||||
return command;
|
||||
}
|
||||
const auto botStatus = peer->isChat()
|
||||
? peer->asChat()->botStatus
|
||||
: peer->isMegagroup()
|
||||
? peer->asChannel()->mgInfo->botStatus
|
||||
: -1;
|
||||
return ((command.indexOf('@') < 2) && (botStatus == 0 || botStatus == 2))
|
||||
? command + '@' + bot->username
|
||||
: command;
|
||||
}
|
||||
|
||||
} // namespace Bot
|
||||
31
Telegram/SourceFiles/chat_helpers/bot_command.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
|
||||
|
||||
class PeerData;
|
||||
class UserData;
|
||||
|
||||
namespace Bot {
|
||||
|
||||
struct SendCommandRequest {
|
||||
not_null<PeerData*> peer;
|
||||
QString command;
|
||||
FullMsgId context;
|
||||
int replyTo = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] QString WrapCommandInChat(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &command,
|
||||
const FullMsgId &context);
|
||||
[[nodiscard]] QString WrapCommandInChat(
|
||||
not_null<PeerData*> peer,
|
||||
const QString &command,
|
||||
not_null<UserData*> bot);
|
||||
|
||||
} // namespace Bot
|
||||
@@ -7,11 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "chat_helpers/bot_keyboard.h"
|
||||
|
||||
#include "core/click_handler_types.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "facades.h"
|
||||
#include "styles/style_widgets.h"
|
||||
@@ -98,9 +100,11 @@ int Style::minButtonWidth(HistoryMessageMarkupButton::Type type) const {
|
||||
|
||||
} // namespace
|
||||
|
||||
BotKeyboard::BotKeyboard(not_null<Main::Session*> session, QWidget *parent)
|
||||
BotKeyboard::BotKeyboard(
|
||||
not_null<Window::SessionController*> controller,
|
||||
QWidget *parent)
|
||||
: TWidget(parent)
|
||||
, _session(session)
|
||||
, _controller(controller)
|
||||
, _st(&st::botKbButton) {
|
||||
setGeometry(0, 0, _st->margin, st::botKbScroll.deltat);
|
||||
_height = st::botKbScroll.deltat;
|
||||
@@ -137,7 +141,12 @@ void BotKeyboard::mouseReleaseEvent(QMouseEvent *e) {
|
||||
updateSelected();
|
||||
|
||||
if (ClickHandlerPtr activated = ClickHandler::unpressed()) {
|
||||
ActivateClickHandler(window(), activated, e->button());
|
||||
ActivateClickHandler(window(), activated, {
|
||||
e->button(),
|
||||
QVariant::fromValue(ClickHandlerContext{
|
||||
.sessionWindow = base::make_weak(_controller.get()),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,32 +160,50 @@ void BotKeyboard::leaveEventHook(QEvent *e) {
|
||||
}
|
||||
|
||||
bool BotKeyboard::moderateKeyActivate(int key) {
|
||||
if (const auto item = _session->data().message(_wasForMsgId)) {
|
||||
const auto &data = _controller->session().data();
|
||||
|
||||
const auto botCommand = [](int key) {
|
||||
if (key == Qt::Key_Q || key == Qt::Key_6) {
|
||||
return u"/translate"_q;
|
||||
} else if (key == Qt::Key_W || key == Qt::Key_5) {
|
||||
return u"/eng"_q;
|
||||
} else if (key == Qt::Key_3) {
|
||||
return u"/pattern"_q;
|
||||
} else if (key == Qt::Key_4) {
|
||||
return u"/abuse"_q;
|
||||
} else if (key == Qt::Key_0 || key == Qt::Key_E || key == Qt::Key_9) {
|
||||
return u"/undo"_q;
|
||||
} else if (key == Qt::Key_Plus
|
||||
|| key == Qt::Key_QuoteLeft
|
||||
|| key == Qt::Key_7) {
|
||||
return u"/next"_q;
|
||||
} else if (key == Qt::Key_Period
|
||||
|| key == Qt::Key_S
|
||||
|| key == Qt::Key_8) {
|
||||
return u"/stats"_q;
|
||||
}
|
||||
return QString();
|
||||
};
|
||||
|
||||
if (const auto item = data.message(_wasForMsgId)) {
|
||||
if (const auto markup = item->Get<HistoryMessageReplyMarkup>()) {
|
||||
if (key >= Qt::Key_1 && key <= Qt::Key_2) {
|
||||
const auto index = int(key - Qt::Key_1);
|
||||
if (!markup->rows.empty()
|
||||
&& index >= 0
|
||||
&& index < int(markup->rows.front().size())) {
|
||||
App::activateBotCommand(item, 0, index);
|
||||
App::activateBotCommand(_controller, item, 0, index);
|
||||
return true;
|
||||
}
|
||||
} else if (const auto user = item->history()->peer->asUser()) {
|
||||
if (user->isBot() && item->from() == user) {
|
||||
if (key == Qt::Key_Q || key == Qt::Key_6) {
|
||||
App::sendBotCommand(user, user, qsl("/translate"));
|
||||
} else if (key == Qt::Key_W || key == Qt::Key_5) {
|
||||
App::sendBotCommand(user, user, qsl("/eng"));
|
||||
} else if (key == Qt::Key_3) {
|
||||
App::sendBotCommand(user, user, qsl("/pattern"));
|
||||
} else if (key == Qt::Key_4) {
|
||||
App::sendBotCommand(user, user, qsl("/abuse"));
|
||||
} else if (key == Qt::Key_0 || key == Qt::Key_E || key == Qt::Key_9) {
|
||||
App::sendBotCommand(user, user, qsl("/undo"));
|
||||
} else if (key == Qt::Key_Plus || key == Qt::Key_QuoteLeft || key == Qt::Key_7) {
|
||||
App::sendBotCommand(user, user, qsl("/next"));
|
||||
} else if (key == Qt::Key_Period || key == Qt::Key_S || key == Qt::Key_8) {
|
||||
App::sendBotCommand(user, user, qsl("/stats"));
|
||||
const auto command = botCommand(key);
|
||||
if (!command.isEmpty()) {
|
||||
_sendCommandRequests.fire({
|
||||
.peer = user,
|
||||
.command = command,
|
||||
.context = item->fullId(),
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -215,9 +242,9 @@ bool BotKeyboard::updateMarkup(HistoryItem *to, bool force) {
|
||||
_wasForMsgId = FullMsgId(to->channelId(), to->id);
|
||||
|
||||
auto markupFlags = to->replyKeyboardFlags();
|
||||
_forceReply = markupFlags & MTPDreplyKeyboardMarkup_ClientFlag::f_force_reply;
|
||||
_maximizeSize = !(markupFlags & MTPDreplyKeyboardMarkup::Flag::f_resize);
|
||||
_singleUse = _forceReply || (markupFlags & MTPDreplyKeyboardMarkup::Flag::f_single_use);
|
||||
_forceReply = markupFlags & ReplyMarkupFlag::ForceReply;
|
||||
_maximizeSize = !(markupFlags & ReplyMarkupFlag::Resize);
|
||||
_singleUse = _forceReply || (markupFlags & ReplyMarkupFlag::SingleUse);
|
||||
|
||||
if (const auto markup = to->Get<HistoryMessageReplyMarkup>()) {
|
||||
_placeholder = markup->placeholder;
|
||||
@@ -317,4 +344,9 @@ void BotKeyboard::updateSelected() {
|
||||
}
|
||||
}
|
||||
|
||||
auto BotKeyboard::sendCommandRequests() const
|
||||
-> rpl::producer<Bot::SendCommandRequest> {
|
||||
return _sendCommandRequests.events();
|
||||
}
|
||||
|
||||
BotKeyboard::~BotKeyboard() = default;
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "chat_helpers/bot_command.h"
|
||||
|
||||
class ReplyKeyboard;
|
||||
|
||||
@@ -15,16 +16,18 @@ namespace style {
|
||||
struct BotKeyboardButton;
|
||||
} // namespace style
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
class BotKeyboard
|
||||
: public TWidget
|
||||
, public Ui::AbstractTooltipShower
|
||||
, public ClickHandlerHost {
|
||||
public:
|
||||
BotKeyboard(not_null<Main::Session*> session, QWidget *parent);
|
||||
BotKeyboard(
|
||||
not_null<Window::SessionController*> controller,
|
||||
QWidget *parent);
|
||||
|
||||
bool moderateKeyActivate(int index);
|
||||
|
||||
@@ -60,6 +63,8 @@ public:
|
||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||
|
||||
rpl::producer<Bot::SendCommandRequest> sendCommandRequests() const;
|
||||
|
||||
~BotKeyboard();
|
||||
|
||||
protected:
|
||||
@@ -78,7 +83,7 @@ private:
|
||||
void updateStyle(int newWidth);
|
||||
void clearSelection();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
FullMsgId _wasForMsgId;
|
||||
QString _placeholder;
|
||||
int _height = 0;
|
||||
@@ -90,6 +95,8 @@ private:
|
||||
QPoint _lastMousePos;
|
||||
std::unique_ptr<ReplyKeyboard> _impl;
|
||||
|
||||
rpl::event_stream<Bot::SendCommandRequest> _sendCommandRequests;
|
||||
|
||||
const style::BotKeyboardButton *_st = nullptr;
|
||||
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/ui_utility.h"
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "layout/layout_utils.h"
|
||||
#include "layout/layout_position.h"
|
||||
#include "emoji_suggestions_data.h"
|
||||
#include "emoji_suggestions_helper.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "chat_helpers/send_context_menu.h" // SendMenu::FillSendMenu
|
||||
#include "core/click_handler_types.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
@@ -27,7 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "inline_bots/inline_bot_result.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "layout/layout_utils.h"
|
||||
#include "layout/layout_position.h"
|
||||
#include "mainwindow.h"
|
||||
#include "main/main_session.h"
|
||||
#include "window/window_session_controller.h"
|
||||
@@ -202,6 +203,11 @@ GifsListWidget::GifsListWidget(
|
||||
) | rpl::start_with_next([=](const QSize &s) {
|
||||
_mosaic.setFullWidth(s.width());
|
||||
}, lifetime());
|
||||
|
||||
_mosaic.setOffset(
|
||||
st::inlineResultsLeft - st::roundRadiusSmall,
|
||||
st::stickerPanPadding);
|
||||
_mosaic.setRightSkip(st::inlineResultsSkip);
|
||||
}
|
||||
|
||||
rpl::producer<TabbedSelector::FileChosen> GifsListWidget::fileChosen() const {
|
||||
@@ -337,12 +343,15 @@ void GifsListWidget::paintInlineItems(Painter &p, QRect clip) {
|
||||
using namespace InlineBots::Layout;
|
||||
PaintContext context(crl::now(), false, gifPaused, false);
|
||||
|
||||
_mosaic.paint(
|
||||
p,
|
||||
st::stickerPanPadding,
|
||||
st::inlineResultsLeft - st::roundRadiusSmall,
|
||||
clip,
|
||||
context);
|
||||
auto paintItem = [&](not_null<const ItemBase*> item, QPoint point) {
|
||||
p.translate(point.x(), point.y());
|
||||
item->paint(
|
||||
p,
|
||||
clip.translated(-point),
|
||||
&context);
|
||||
p.translate(-point.x(), -point.y());
|
||||
};
|
||||
_mosaic.paint(std::move(paintItem), clip);
|
||||
}
|
||||
|
||||
void GifsListWidget::mousePressEvent(QMouseEvent *e) {
|
||||
@@ -407,7 +416,12 @@ void GifsListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (dynamic_cast<InlineBots::Layout::SendClickHandler*>(activated.get())) {
|
||||
selectInlineResult(_selected, {});
|
||||
} else {
|
||||
ActivateClickHandler(window(), activated, e->button());
|
||||
ActivateClickHandler(window(), activated, {
|
||||
e->button(),
|
||||
QVariant::fromValue(ClickHandlerContext{
|
||||
.sessionWindow = base::make_weak(controller().get()),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,7 +534,7 @@ void GifsListWidget::refreshSavedGifs() {
|
||||
return layoutPrepareSavedGif(gif);
|
||||
}) | ranges::views::filter([](const LayoutItem *item) {
|
||||
return item != nullptr;
|
||||
}) | ranges::to_vector;
|
||||
}) | ranges::to<std::vector<not_null<LayoutItem*>>>;
|
||||
|
||||
_mosaic.addItems(layouts);
|
||||
}
|
||||
@@ -610,7 +624,9 @@ void GifsListWidget::deleteUnusedInlineLayouts() {
|
||||
}
|
||||
|
||||
void GifsListWidget::preloadImages() {
|
||||
_mosaic.preloadImages();
|
||||
_mosaic.forEach([](not_null<const LayoutItem*> item) {
|
||||
item->preload();
|
||||
});
|
||||
}
|
||||
|
||||
void GifsListWidget::switchToSavedGifs() {
|
||||
@@ -645,10 +661,11 @@ int GifsListWidget::refreshInlineRows(const InlineCacheEntry *entry, bool result
|
||||
return layoutPrepareInlineResult(r.get());
|
||||
}) | ranges::views::filter([](const LayoutItem *item) {
|
||||
return item != nullptr;
|
||||
}) | ranges::to_vector;
|
||||
}) | ranges::to<std::vector<not_null<LayoutItem*>>>;
|
||||
|
||||
_mosaic.addItems(resultLayouts);
|
||||
added = resultLayouts.size();
|
||||
preloadImages();
|
||||
}
|
||||
|
||||
resizeToWidth(width());
|
||||
@@ -661,7 +678,11 @@ int GifsListWidget::refreshInlineRows(const InlineCacheEntry *entry, bool result
|
||||
}
|
||||
|
||||
int GifsListWidget::validateExistingInlineRows(const InlineResults &results) {
|
||||
const auto until = _mosaic.validateExistingRows(results);
|
||||
const auto until = _mosaic.validateExistingRows([&](
|
||||
not_null<const LayoutItem*> item,
|
||||
int untilIndex) {
|
||||
return item->getResult() != results[untilIndex].get();
|
||||
}, results.size());
|
||||
|
||||
if (_mosaic.empty()) {
|
||||
_inlineWithThumb = false;
|
||||
@@ -853,10 +874,12 @@ void GifsListWidget::updateSelected() {
|
||||
}
|
||||
|
||||
const auto p = mapFromGlobal(_lastMousePos);
|
||||
const auto sx = (rtl() ? width() - p.x() : p.x())
|
||||
- (st::inlineResultsLeft - st::roundRadiusSmall);
|
||||
const auto sy = p.y() - st::stickerPanPadding;
|
||||
const auto &[link, item, selected] = _mosaic.findByPoint({ sx, sy });
|
||||
const auto sx = rtl() ? (width() - p.x()) : p.x();
|
||||
const auto sy = p.y();
|
||||
const auto &[index, exact, relative] = _mosaic.findByPoint({ sx, sy });
|
||||
const auto selected = exact ? index : -1;
|
||||
const auto item = exact ? _mosaic.itemAt(selected).get() : nullptr;
|
||||
const auto link = exact ? item->getState(relative, {}).link : nullptr;
|
||||
|
||||
if (_selected != selected) {
|
||||
if (const auto s = _mosaic.maybeItemAt(_selected)) {
|
||||
|
||||
@@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "chat_helpers/tabbed_selector.h"
|
||||
#include "base/timer.h"
|
||||
#include "inline_bots/inline_bot_layout_item.h"
|
||||
#include "inline_bots/inline_results_mosaic_layout.h"
|
||||
#include "layout/layout_mosaic.h"
|
||||
#include "app.h"
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
@@ -167,7 +167,7 @@ private:
|
||||
|
||||
Footer *_footer = nullptr;
|
||||
|
||||
InlineBots::Layout::MosaicLayout _mosaic;
|
||||
Mosaic::Layout::MosaicLayout<LayoutItem> _mosaic;
|
||||
|
||||
int _selected = -1;
|
||||
int _pressed = -1;
|
||||
|
||||
@@ -269,7 +269,7 @@ void Application::run() {
|
||||
|
||||
const auto currentGeometry = _window->widget()->geometry();
|
||||
_mediaView = std::make_unique<Media::View::OverlayWidget>();
|
||||
_window->widget()->setGeometry(currentGeometry);
|
||||
_window->widget()->Ui::RpWidget::setGeometry(currentGeometry);
|
||||
|
||||
DEBUG_LOG(("Application Info: showing."));
|
||||
_window->finishFirstShow();
|
||||
@@ -392,15 +392,6 @@ bool Application::hideMediaView() {
|
||||
return false;
|
||||
}
|
||||
|
||||
PeerData *Application::ui_getPeerForMouseAction() {
|
||||
if (_mediaView && !_mediaView->isHidden()) {
|
||||
return _mediaView->ui_getPeerForMouseAction();
|
||||
} else if (const auto m = App::main()) { // multi good
|
||||
return m->ui_getPeerForMouseAction();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Application::eventFilter(QObject *object, QEvent *e) {
|
||||
switch (e->type()) {
|
||||
case QEvent::KeyPress:
|
||||
|
||||
@@ -143,7 +143,6 @@ public:
|
||||
// Media view interface.
|
||||
void checkMediaViewActivation();
|
||||
bool hideMediaView();
|
||||
[[nodiscard]] PeerData *ui_getPeerForMouseAction();
|
||||
|
||||
[[nodiscard]] QPoint getPointForCallPanelCenter() const;
|
||||
[[nodiscard]] QImage logo() const {
|
||||
|
||||
@@ -11,10 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/application.h"
|
||||
#include "core/local_url_handlers.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "main/main_session.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "base/qthelp_regex.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "history/history.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/history_item.h"
|
||||
#include "data/data_user.h"
|
||||
@@ -25,6 +27,36 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace {
|
||||
|
||||
void SearchByHashtag(ClickContext context, const QString &tag) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
const auto controller = my.sessionWindow.get();
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
if (controller->openedFolder().current()) {
|
||||
controller->closeFolder();
|
||||
}
|
||||
|
||||
controller->widget()->ui_hideSettingsAndLayer(anim::type::normal);
|
||||
Core::App().hideMediaView();
|
||||
|
||||
auto &data = controller->session().data();
|
||||
const auto inPeer = my.peer
|
||||
? my.peer
|
||||
: my.itemId
|
||||
? data.message(my.itemId)->history()->peer.get()
|
||||
: nullptr;
|
||||
controller->content()->searchMessages(
|
||||
tag + ' ',
|
||||
(inPeer && !inPeer->isUser())
|
||||
? data.history(inPeer).get()
|
||||
: Dialogs::Key());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool UrlRequiresConfirmation(const QUrl &url) {
|
||||
using namespace qthelp;
|
||||
|
||||
@@ -159,7 +191,7 @@ QString HashtagClickHandler::copyToClipboardContextItemText() const {
|
||||
void HashtagClickHandler::onClick(ClickContext context) const {
|
||||
const auto button = context.button;
|
||||
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
|
||||
App::searchByHashtag(_tag, Ui::getPeerForMouseAction());
|
||||
SearchByHashtag(context, _tag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +206,7 @@ QString CashtagClickHandler::copyToClipboardContextItemText() const {
|
||||
void CashtagClickHandler::onClick(ClickContext context) const {
|
||||
const auto button = context.button;
|
||||
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
|
||||
App::searchByHashtag(_tag, Ui::getPeerForMouseAction());
|
||||
SearchByHashtag(context, _tag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,24 +216,31 @@ auto CashtagClickHandler::getTextEntity() const -> TextEntity {
|
||||
|
||||
void BotCommandClickHandler::onClick(ClickContext context) const {
|
||||
const auto button = context.button;
|
||||
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto delegate = my.elementDelegate ? my.elementDelegate() : nullptr) {
|
||||
delegate->elementSendBotCommand(_cmd, my.itemId);
|
||||
if (button != Qt::LeftButton && button != Qt::MiddleButton) {
|
||||
return;
|
||||
}
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto delegate = my.elementDelegate ? my.elementDelegate() : nullptr) {
|
||||
delegate->elementSendBotCommand(_cmd, my.itemId);
|
||||
} else if (const auto controller = my.sessionWindow.get()) {
|
||||
auto &data = controller->session().data();
|
||||
const auto peer = my.peer
|
||||
? my.peer
|
||||
: my.itemId
|
||||
? data.message(my.itemId)->history()->peer.get()
|
||||
: nullptr;
|
||||
// Can't find context.
|
||||
if (!peer) {
|
||||
return;
|
||||
} else if (auto peer = Ui::getPeerForMouseAction()) { // old way
|
||||
auto bot = peer->isUser() ? peer->asUser() : nullptr;
|
||||
if (!bot) {
|
||||
if (const auto view = App::hoveredLinkItem()) {
|
||||
// may return nullptr
|
||||
bot = view->data()->fromOriginal()->asUser();
|
||||
}
|
||||
}
|
||||
Ui::showPeerHistory(peer, ShowAtTheEndMsgId);
|
||||
App::sendBotCommand(peer, bot, _cmd);
|
||||
} else {
|
||||
App::insertBotCommand(_cmd);
|
||||
}
|
||||
controller->widget()->ui_hideSettingsAndLayer(anim::type::normal);
|
||||
Core::App().hideMediaView();
|
||||
controller->content()->sendBotCommand({
|
||||
.peer = peer,
|
||||
.command = _cmd,
|
||||
.context = my.itemId,
|
||||
.replyTo = 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,15 @@ class SessionController;
|
||||
|
||||
[[nodiscard]] bool UrlRequiresConfirmation(const QUrl &url);
|
||||
|
||||
class PeerData;
|
||||
struct ClickHandlerContext {
|
||||
FullMsgId itemId;
|
||||
// Is filled from sections.
|
||||
Fn<HistoryView::ElementDelegate*()> elementDelegate;
|
||||
base::weak_ptr<Window::SessionController> sessionWindow;
|
||||
bool skipBotAutoLogin = false;
|
||||
// Is filled from peer info.
|
||||
PeerData *peer = nullptr;
|
||||
};
|
||||
Q_DECLARE_METATYPE(ClickHandlerContext);
|
||||
|
||||
@@ -169,7 +173,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class PeerData;
|
||||
class BotCommandClickHandler : public TextClickHandler {
|
||||
public:
|
||||
BotCommandClickHandler(const QString &cmd) : _cmd(cmd) {
|
||||
|
||||
@@ -314,6 +314,7 @@ CloudPasswordState ParseCloudPasswordState(
|
||||
ParseSecureSecretAlgo(data.vnew_secure_algo()));
|
||||
result.unconfirmedPattern =
|
||||
qs(data.vemail_unconfirmed_pattern().value_or_empty());
|
||||
result.pendingResetDate = data.vpending_reset_date().value_or_empty();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ struct CloudPasswordState {
|
||||
CloudPasswordAlgo newPassword;
|
||||
SecureSecretAlgo newSecureSecret;
|
||||
QString unconfirmedPattern;
|
||||
TimeId pendingResetDate = 0;
|
||||
};
|
||||
|
||||
CloudPasswordState ParseCloudPasswordState(
|
||||
|
||||
@@ -112,8 +112,7 @@ QByteArray Settings::serialize() const {
|
||||
+ sizeof(qint64)
|
||||
+ sizeof(qint32) * 2
|
||||
+ Serialize::bytearraySize(windowPosition)
|
||||
+ sizeof(qint32)
|
||||
+ Serialize::bytearraySize(_photoEditorBrush);
|
||||
+ sizeof(qint32);
|
||||
for (const auto &[id, rating] : recentEmojiPreloadData) {
|
||||
size += Serialize::stringSize(id) + sizeof(quint16);
|
||||
}
|
||||
@@ -123,7 +122,9 @@ QByteArray Settings::serialize() const {
|
||||
}
|
||||
size += sizeof(qint32) * 3
|
||||
+ Serialize::bytearraySize(proxy)
|
||||
+ sizeof(qint32) * 2;
|
||||
+ sizeof(qint32) * 2
|
||||
+ Serialize::bytearraySize(_photoEditorBrush)
|
||||
+ sizeof(qint32);
|
||||
|
||||
auto result = QByteArray();
|
||||
result.reserve(size);
|
||||
@@ -212,12 +213,13 @@ QByteArray Settings::serialize() const {
|
||||
}
|
||||
stream
|
||||
<< qint32(0) // Old Disable OpenGL
|
||||
<< qint32(_groupCallNoiseSuppression ? 1 : 0)
|
||||
<< qint32(0) // Old Noise Suppression
|
||||
<< qint32(_workMode.current())
|
||||
<< proxy
|
||||
<< qint32(_hiddenGroupCallTooltips.value())
|
||||
<< qint32(_disableOpenGL ? 1 : 0)
|
||||
<< _photoEditorBrush;
|
||||
<< _photoEditorBrush
|
||||
<< qint32(_groupCallNoiseSuppression ? 1 : 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -432,7 +434,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
stream >> disableOpenGLOld;
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
stream >> groupCallNoiseSuppression;
|
||||
qint32 groupCallNoiseSuppressionOld;
|
||||
stream >> groupCallNoiseSuppressionOld;
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
stream >> workMode;
|
||||
@@ -449,6 +452,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
if (!stream.atEnd()) {
|
||||
stream >> photoEditorBrush;
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
stream >> groupCallNoiseSuppression;
|
||||
}
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("App Error: "
|
||||
"Bad data for Core::Settings::constructFromSerialized()"));
|
||||
@@ -802,7 +808,7 @@ void Settings::resetOnLastLogout() {
|
||||
_groupCallPushToTalkShortcut = QByteArray();
|
||||
_groupCallPushToTalkDelay = 20;
|
||||
|
||||
_groupCallNoiseSuppression = true;
|
||||
_groupCallNoiseSuppression = false;
|
||||
|
||||
//_themesAccentColors = Window::Theme::AccentColors();
|
||||
|
||||
|
||||
@@ -653,7 +653,7 @@ private:
|
||||
bool _callAudioDuckingEnabled = true;
|
||||
bool _disableCalls = false;
|
||||
bool _groupCallPushToTalk = false;
|
||||
bool _groupCallNoiseSuppression = true;
|
||||
bool _groupCallNoiseSuppression = false;
|
||||
QByteArray _groupCallPushToTalkShortcut;
|
||||
crl::time _groupCallPushToTalkDelay = 20;
|
||||
Window::Theme::AccentColors _themesAccentColors;
|
||||
|
||||
@@ -297,8 +297,6 @@ QString PlatformString() {
|
||||
return qsl("Windows64Bit");
|
||||
} else if (Platform::IsMacStoreBuild()) {
|
||||
return qsl("MacAppStore");
|
||||
} else if (Platform::IsOSXBuild()) {
|
||||
return qsl("OSX");
|
||||
} else if (Platform::IsMac()) {
|
||||
return qsl("MacOS");
|
||||
} else if (Platform::IsLinux32Bit()) {
|
||||
|
||||
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
|
||||
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
|
||||
constexpr auto AppName = "Telegram Desktop"_cs;
|
||||
constexpr auto AppFile = "Telegram"_cs;
|
||||
constexpr auto AppVersion = 2008012;
|
||||
constexpr auto AppVersionStr = "2.8.12";
|
||||
constexpr auto AppBetaVersion = true;
|
||||
constexpr auto AppVersion = 2009003;
|
||||
constexpr auto AppVersionStr = "2.9.3";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
@@ -30,7 +30,7 @@ constexpr auto kMessagesPerPage = 50;
|
||||
const QString &text) {
|
||||
return history->makeServiceMessage(
|
||||
history->session().data().nextNonHistoryEntryId(),
|
||||
MTPDmessage_ClientFlag::f_fake_history_item,
|
||||
MessageFlag::FakeHistoryItem,
|
||||
date,
|
||||
HistoryService::PreparedText{ text });
|
||||
}
|
||||
@@ -538,7 +538,7 @@ bool RepliesList::processMessagesIsEmpty(const MTPmessages_Messages &result) {
|
||||
const auto maxId = IdFromMessage(list.front());
|
||||
const auto wasSize = int(_list.size());
|
||||
const auto toFront = (wasSize > 0) && (maxId > _list.front());
|
||||
const auto clientFlags = MTPDmessage_ClientFlags();
|
||||
const auto localFlags = MessageFlags();
|
||||
const auto type = NewMessageType::Existing;
|
||||
auto refreshed = std::vector<MsgId>();
|
||||
if (toFront) {
|
||||
@@ -546,7 +546,7 @@ bool RepliesList::processMessagesIsEmpty(const MTPmessages_Messages &result) {
|
||||
}
|
||||
auto skipped = 0;
|
||||
for (const auto &message : list) {
|
||||
if (const auto item = owner.addNewMessage(message, clientFlags, type)) {
|
||||
if (const auto item = owner.addNewMessage(message, localFlags, type)) {
|
||||
if (item->replyToTop() == _rootId) {
|
||||
if (toFront) {
|
||||
refreshed.push_back(item->id);
|
||||
|
||||
@@ -158,6 +158,7 @@ void ScheduledMessages::sendNowSimpleMessage(
|
||||
not_null<HistoryItem*> local) {
|
||||
Expects(local->isSending());
|
||||
Expects(local->isScheduled());
|
||||
|
||||
if (HasScheduledDate(local)) {
|
||||
LOG(("Error: trying to put to history a new local message, "
|
||||
"that has scheduled date."));
|
||||
@@ -175,17 +176,19 @@ void ScheduledMessages::sendNowSimpleMessage(
|
||||
auto action = Api::SendAction(history);
|
||||
action.replyTo = local->replyToId();
|
||||
const auto replyHeader = NewMessageReplyHeader(action);
|
||||
auto flags = NewMessageFlags(history->peer)
|
||||
| MTPDmessage::Flag::f_entities
|
||||
const auto localFlags = NewMessageFlags(history->peer)
|
||||
| MessageFlag::LocalHistoryEntry;
|
||||
const auto flags = MTPDmessage::Flag::f_entities
|
||||
| MTPDmessage::Flag::f_from_id
|
||||
| (local->replyToId()
|
||||
? MTPDmessage::Flag::f_reply_to
|
||||
: MTPDmessage::Flag(0))
|
||||
| (update.vttl_period()
|
||||
? MTPDmessage::Flag::f_ttl_period
|
||||
: MTPDmessage::Flag(0))
|
||||
| ((localFlags & MessageFlag::Outgoing)
|
||||
? MTPDmessage::Flag::f_out
|
||||
: MTPDmessage::Flag(0));
|
||||
auto clientFlags = NewMessageClientFlags()
|
||||
| MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
const auto views = 1;
|
||||
const auto forwards = 0;
|
||||
history->addNewMessage(
|
||||
@@ -213,7 +216,7 @@ void ScheduledMessages::sendNowSimpleMessage(
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>(),
|
||||
MTP_int(update.vttl_period().value_or_empty())),
|
||||
clientFlags,
|
||||
localFlags,
|
||||
NewMessageType::Unread);
|
||||
|
||||
local->destroy();
|
||||
@@ -459,7 +462,7 @@ HistoryItem *ScheduledMessages::append(
|
||||
|
||||
const auto item = _session->data().addNewMessage(
|
||||
PrepareMessage(message, history->nextNonHistoryEntryId()),
|
||||
MTPDmessage_ClientFlags(),
|
||||
MessageFlags(), // localFlags
|
||||
NewMessageType::Existing);
|
||||
if (!item || item->history() != history) {
|
||||
LOG(("API Error: Bad data received in scheduled messages."));
|
||||
|
||||
@@ -161,7 +161,7 @@ SearchResult ParseSearchResult(
|
||||
for (const auto &message : *messages) {
|
||||
const auto item = peer->owner().addNewMessage(
|
||||
message,
|
||||
MTPDmessage_ClientFlags(),
|
||||
MessageFlags(),
|
||||
addType);
|
||||
if (item) {
|
||||
const auto itemId = item->id;
|
||||
|
||||
@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/crash_reports.h" // CrashReports::SetAnnotation
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/image/image_location_factory.h" // Images::FromPhotoSize
|
||||
#include "ui/text/format_values.h" // Ui::FormatPhone
|
||||
#include "export/export_manager.h"
|
||||
#include "export/view/export_view_panel_controller.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
@@ -506,7 +507,7 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
|
||||
|
||||
const auto pname = (showPhoneChanged || phoneChanged || nameChanged)
|
||||
? ((showPhone && !phone.isEmpty())
|
||||
? App::formatPhone(phone)
|
||||
? Ui::FormatPhone(phone)
|
||||
: QString())
|
||||
: result->nameOrPhone;
|
||||
|
||||
@@ -1963,7 +1964,7 @@ void Session::processMessages(
|
||||
for (const auto &[position, index] : indices) {
|
||||
addNewMessage(
|
||||
data[index],
|
||||
MTPDmessage_ClientFlags(),
|
||||
MessageFlags(),
|
||||
type);
|
||||
}
|
||||
}
|
||||
@@ -2019,8 +2020,11 @@ void Session::scheduleNextTTLs() {
|
||||
}
|
||||
const auto nearest = _ttlMessages.begin()->first;
|
||||
const auto now = base::unixtime::now();
|
||||
const auto timeout = (std::max(now, nearest) - now) * crl::time(1000);
|
||||
_ttlCheckTimer.callOnce(timeout);
|
||||
|
||||
// Set timer not more than for 24 hours.
|
||||
const auto maxTimeout = TimeId(86400);
|
||||
const auto timeout = std::min(std::max(now, nearest) - now, maxTimeout);
|
||||
_ttlCheckTimer.callOnce(timeout * crl::time(1000));
|
||||
}
|
||||
|
||||
void Session::unregisterMessageTTL(
|
||||
@@ -2280,7 +2284,7 @@ void Session::unmuteByFinished() {
|
||||
|
||||
HistoryItem *Session::addNewMessage(
|
||||
const MTPMessage &data,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags localFlags,
|
||||
NewMessageType type) {
|
||||
const auto peerId = PeerFromMessage(data);
|
||||
if (!peerId) {
|
||||
@@ -2289,7 +2293,7 @@ HistoryItem *Session::addNewMessage(
|
||||
|
||||
const auto result = history(peerId)->addNewMessage(
|
||||
data,
|
||||
clientFlags,
|
||||
localFlags,
|
||||
type);
|
||||
if (result && type == NewMessageType::Unread) {
|
||||
CheckForSwitchInlineButton(result);
|
||||
@@ -3587,7 +3591,7 @@ QString Session::findContactPhone(not_null<UserData*> contact) const {
|
||||
const auto result = contact->phone();
|
||||
return result.isEmpty()
|
||||
? findContactPhone(peerToUser(contact->id))
|
||||
: App::formatPhone(result);
|
||||
: Ui::FormatPhone(result);
|
||||
}
|
||||
|
||||
QString Session::findContactPhone(UserId contactId) const {
|
||||
@@ -4061,8 +4065,8 @@ void Session::insertCheckedServiceNotification(
|
||||
const auto flags = MTPDmessage::Flag::f_entities
|
||||
| MTPDmessage::Flag::f_from_id
|
||||
| MTPDmessage::Flag::f_media;
|
||||
const auto clientFlags = MTPDmessage_ClientFlag::f_clientside_unread
|
||||
| MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
const auto localFlags = MessageFlag::ClientSideUnread
|
||||
| MessageFlag::LocalHistoryEntry;
|
||||
auto sending = TextWithEntities(), left = message;
|
||||
while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
|
||||
addNewMessage(
|
||||
@@ -4088,7 +4092,7 @@ void Session::insertCheckedServiceNotification(
|
||||
//MTPMessageReactions(),
|
||||
MTPVector<MTPRestrictionReason>(),
|
||||
MTPint()), // ttl_period
|
||||
clientFlags,
|
||||
localFlags,
|
||||
NewMessageType::Unread);
|
||||
}
|
||||
sendHistoryChangeNotifications();
|
||||
|
||||
@@ -400,7 +400,7 @@ public:
|
||||
|
||||
HistoryItem *addNewMessage(
|
||||
const MTPMessage &data,
|
||||
MTPDmessage_ClientFlags flags,
|
||||
MessageFlags localFlags,
|
||||
NewMessageType type);
|
||||
|
||||
struct SendActionAnimationUpdate {
|
||||
|
||||
@@ -364,3 +364,61 @@ struct StickerSetIdentifier {
|
||||
return !empty();
|
||||
}
|
||||
};
|
||||
|
||||
enum class MessageFlag : uint32 {
|
||||
HideEdited = (1U << 0),
|
||||
Legacy = (1U << 1),
|
||||
HasReplyMarkup = (1U << 2),
|
||||
HasFromId = (1U << 3),
|
||||
HasPostAuthor = (1U << 4),
|
||||
HasViews = (1U << 5),
|
||||
HasReplyInfo = (1U << 6),
|
||||
HasViaBot = (1U << 7),
|
||||
AdminLogEntry = (1U << 8),
|
||||
Post = (1U << 9),
|
||||
Silent = (1U << 10),
|
||||
Outgoing = (1U << 11),
|
||||
Pinned = (1U << 12),
|
||||
MediaIsUnread = (1U << 13),
|
||||
MentionsMe = (1U << 14),
|
||||
IsOrWasScheduled = (1U << 15),
|
||||
|
||||
// Needs to return back to inline mode.
|
||||
HasSwitchInlineButton = (1U << 16),
|
||||
|
||||
// For "shared links" indexing.
|
||||
HasTextLinks = (1U << 17),
|
||||
|
||||
// Group / channel create or migrate service message.
|
||||
IsGroupEssential = (1U << 18),
|
||||
|
||||
// Edited media is generated on the client
|
||||
// and should not update media from server.
|
||||
IsLocalUpdateMedia = (1U << 19),
|
||||
|
||||
// Sent from inline bot, need to re-set media when sent.
|
||||
FromInlineBot = (1U << 20),
|
||||
|
||||
// Generated on the client side and should be unread.
|
||||
ClientSideUnread = (1U << 21),
|
||||
|
||||
// In a supergroup.
|
||||
HasAdminBadge = (1U << 22),
|
||||
|
||||
// Outgoing message that is being sent.
|
||||
BeingSent = (1U << 23),
|
||||
|
||||
// Outgoing message and failed to be sent.
|
||||
SendingFailed = (1U << 24),
|
||||
|
||||
// No media and only a several emoji text.
|
||||
IsolatedEmoji = (1U << 25),
|
||||
|
||||
// Local message existing in the message history.
|
||||
LocalHistoryEntry = (1U << 26),
|
||||
|
||||
// Fake message for some UI element.
|
||||
FakeHistoryItem = (1U << 27),
|
||||
};
|
||||
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
||||
using MessageFlags = base::flags<MessageFlag>;
|
||||
|
||||
@@ -2047,7 +2047,7 @@ bool InnerWidget::searchReceived(
|
||||
if (lastDate) {
|
||||
const auto item = session().data().addNewMessage(
|
||||
message,
|
||||
MTPDmessage_ClientFlags(),
|
||||
MessageFlags(),
|
||||
NewMessageType::Existing);
|
||||
const auto history = item->history();
|
||||
if (!uniquePeers || !hasHistoryInResults(history)) {
|
||||
|
||||
@@ -21,10 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <range/v3/view/transform.hpp>
|
||||
#include <range/v3/range/conversion.hpp>
|
||||
|
||||
namespace App { // Hackish..
|
||||
QString formatPhone(QString phone);
|
||||
} // namespace App
|
||||
|
||||
namespace Export {
|
||||
namespace Data {
|
||||
namespace {
|
||||
@@ -1795,7 +1791,7 @@ bool SkipMessageByDate(const Message &message, const Settings &settings) {
|
||||
Utf8String FormatPhoneNumber(const Utf8String &phoneNumber) {
|
||||
return phoneNumber.isEmpty()
|
||||
? Utf8String()
|
||||
: App::formatPhone(QString::fromUtf8(phoneNumber)).toUtf8();
|
||||
: Ui::FormatPhone(QString::fromUtf8(phoneNumber)).toUtf8();
|
||||
}
|
||||
|
||||
Utf8String FormatDateTime(
|
||||
|
||||
@@ -58,15 +58,6 @@ namespace {
|
||||
|
||||
namespace App {
|
||||
|
||||
void sendBotCommand(
|
||||
not_null<PeerData*> peer,
|
||||
UserData *bot,
|
||||
const QString &cmd, MsgId replyTo) {
|
||||
if (const auto m = CheckMainWidget(&peer->session())) {
|
||||
m->sendBotCommand(peer, bot, cmd, replyTo);
|
||||
}
|
||||
}
|
||||
|
||||
void hideSingleUseKeyboard(not_null<const HistoryItem*> message) {
|
||||
if (const auto m = CheckMainWidget(&message->history()->session())) {
|
||||
m->hideSingleUseKeyboard(message->history()->peer, message->id);
|
||||
@@ -81,6 +72,7 @@ bool insertBotCommand(const QString &cmd) {
|
||||
}
|
||||
|
||||
void activateBotCommand(
|
||||
Window::SessionController *sessionController,
|
||||
not_null<const HistoryItem*> msg,
|
||||
int row,
|
||||
int column) {
|
||||
@@ -98,12 +90,15 @@ void activateBotCommand(
|
||||
case ButtonType::Default: {
|
||||
// Copy string before passing it to the sending method
|
||||
// because the original button can be destroyed inside.
|
||||
MsgId replyTo = (msg->id > 0) ? msg->id : 0;
|
||||
sendBotCommand(
|
||||
msg->history()->peer,
|
||||
msg->fromOriginal()->asUser(),
|
||||
QString(button->text),
|
||||
replyTo);
|
||||
if (sessionController) {
|
||||
MsgId replyTo = (msg->id > 0) ? msg->id : 0;
|
||||
sessionController->content()->sendBotCommand({
|
||||
.peer = msg->history()->peer,
|
||||
.command = QString(button->text),
|
||||
.context = msg->fullId(),
|
||||
.replyTo = replyTo,
|
||||
});
|
||||
}
|
||||
} break;
|
||||
|
||||
case ButtonType::Callback:
|
||||
@@ -215,24 +210,6 @@ void activateBotCommand(
|
||||
}
|
||||
}
|
||||
|
||||
void searchByHashtag(const QString &tag, PeerData *inPeer) {
|
||||
const auto m = inPeer
|
||||
? CheckMainWidget(&inPeer->session())
|
||||
: App::main(); // multi good
|
||||
if (m) {
|
||||
if (m->controller()->openedFolder().current()) {
|
||||
m->controller()->closeFolder();
|
||||
}
|
||||
Ui::hideSettingsAndLayer();
|
||||
Core::App().hideMediaView();
|
||||
m->searchMessages(
|
||||
tag + ' ',
|
||||
(inPeer && !inPeer->isUser())
|
||||
? inPeer->owner().history(inPeer).get()
|
||||
: Dialogs::Key());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace App
|
||||
|
||||
namespace Ui {
|
||||
@@ -282,10 +259,6 @@ void showPeerHistory(not_null<const PeerData*> peer, MsgId msgId) {
|
||||
}
|
||||
}
|
||||
|
||||
PeerData *getPeerForMouseAction() {
|
||||
return Core::App().ui_getPeerForMouseAction();
|
||||
}
|
||||
|
||||
bool skipPaintEvent(QWidget *widget, QPaintEvent *event) {
|
||||
if (auto w = App::wnd()) {
|
||||
if (w->contentOverlapped(widget, event)) {
|
||||
|
||||
@@ -14,15 +14,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class History;
|
||||
|
||||
namespace Data {
|
||||
struct FileOrigin;
|
||||
} // namespace Data
|
||||
|
||||
namespace InlineBots {
|
||||
namespace Layout {
|
||||
class ItemBase;
|
||||
} // namespace Layout
|
||||
} // namespace InlineBots
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace App {
|
||||
|
||||
@@ -37,17 +31,12 @@ template <typename Guard, typename Lambda>
|
||||
};
|
||||
}
|
||||
|
||||
void sendBotCommand(
|
||||
not_null<PeerData*> peer,
|
||||
UserData *bot,
|
||||
const QString &cmd,
|
||||
MsgId replyTo = 0);
|
||||
bool insertBotCommand(const QString &cmd);
|
||||
void activateBotCommand(
|
||||
Window::SessionController *sessionController,
|
||||
not_null<const HistoryItem*> msg,
|
||||
int row,
|
||||
int column);
|
||||
void searchByHashtag(const QString &tag, PeerData *inPeer);
|
||||
|
||||
} // namespace App
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/application.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_attached_stickers.h"
|
||||
#include "layout.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
@@ -36,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/inactive_press.h"
|
||||
#include "ui/effects/path_shift_gradient.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "boxes/peers/edit_participant_box.h"
|
||||
@@ -657,6 +657,9 @@ not_null<Ui::PathShiftGradient*> InnerWidget::elementPathShiftGradient() {
|
||||
return _pathGradient.get();
|
||||
}
|
||||
|
||||
void InnerWidget::elementReplyTo(const FullMsgId &to) {
|
||||
}
|
||||
|
||||
void InnerWidget::saveState(not_null<SectionMemento*> memento) {
|
||||
memento->setFilter(std::move(_filter));
|
||||
memento->setAdmins(std::move(_admins));
|
||||
@@ -1559,7 +1562,17 @@ void InnerWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton but
|
||||
|
||||
if (activated) {
|
||||
mouseActionCancel();
|
||||
ActivateClickHandler(window(), activated, button);
|
||||
ActivateClickHandler(window(), activated, {
|
||||
button,
|
||||
QVariant::fromValue(ClickHandlerContext{
|
||||
.elementDelegate = [weak = Ui::MakeWeak(this)] {
|
||||
return weak
|
||||
? (ElementDelegate*)weak
|
||||
: nullptr;
|
||||
},
|
||||
.sessionWindow = base::make_weak(_controller.get()),
|
||||
})
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && button != Qt::RightButton) {
|
||||
|
||||
@@ -132,6 +132,7 @@ public:
|
||||
void elementHandleViaClick(not_null<UserData*> bot) override;
|
||||
bool elementIsChatWide() override;
|
||||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
||||
void elementReplyTo(const FullMsgId &to) override;
|
||||
|
||||
~InnerWidget();
|
||||
|
||||
|
||||
@@ -510,7 +510,6 @@ void GenerateItems(
|
||||
return callback(OwnedItem(delegate, item), sentDate);
|
||||
};
|
||||
|
||||
using Flag = MTPDmessage::Flag;
|
||||
const auto fromName = from->name;
|
||||
const auto fromLink = from->createOpenLink();
|
||||
const auto fromLinkText = textcmdLink(1, fromName);
|
||||
@@ -520,10 +519,9 @@ void GenerateItems(
|
||||
message.links.push_back(fromLink);
|
||||
addPart(history->makeServiceMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
MTPDmessage_ClientFlag::f_admin_log_entry,
|
||||
MessageFlag::AdminLogEntry,
|
||||
date,
|
||||
message,
|
||||
MTPDmessage::Flags(0),
|
||||
peerToUser(from->id),
|
||||
photo));
|
||||
};
|
||||
@@ -540,6 +538,29 @@ void GenerateItems(
|
||||
addSimpleServiceMessage(text);
|
||||
};
|
||||
|
||||
auto makeSimpleTextMessage = [&](TextWithEntities &&text) {
|
||||
auto bodyFlags = MessageFlag::HasFromId | MessageFlag::AdminLogEntry;
|
||||
auto bodyReplyTo = MsgId();
|
||||
auto bodyViaBotId = UserId();
|
||||
auto bodyGroupedId = uint64();
|
||||
return history->makeMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
bodyFlags,
|
||||
bodyReplyTo,
|
||||
bodyViaBotId,
|
||||
date,
|
||||
peerToUser(from->id),
|
||||
QString(),
|
||||
std::move(text),
|
||||
MTP_messageMediaEmpty(),
|
||||
MTPReplyMarkup(),
|
||||
bodyGroupedId);
|
||||
};
|
||||
|
||||
auto addSimpleTextMessage = [&](TextWithEntities &&text) {
|
||||
addPart(makeSimpleTextMessage(std::move(text)));
|
||||
};
|
||||
|
||||
auto createChangeAbout = [&](const MTPDchannelAdminLogEventActionChangeAbout &action) {
|
||||
auto newValue = qs(action.vnew_value());
|
||||
auto oldValue = qs(action.vprev_value());
|
||||
@@ -553,21 +574,8 @@ void GenerateItems(
|
||||
)(tr::now, lt_from, fromLinkText);
|
||||
addSimpleServiceMessage(text);
|
||||
|
||||
auto bodyFlags = Flag::f_entities | Flag::f_from_id;
|
||||
auto bodyClientFlags = MTPDmessage_ClientFlag::f_admin_log_entry;
|
||||
auto bodyReplyTo = 0;
|
||||
auto bodyViaBotId = 0;
|
||||
auto newDescription = PrepareText(newValue, QString());
|
||||
auto body = history->makeMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
bodyFlags,
|
||||
bodyClientFlags,
|
||||
bodyReplyTo,
|
||||
bodyViaBotId,
|
||||
date,
|
||||
peerToUser(from->id),
|
||||
QString(),
|
||||
newDescription);
|
||||
auto body = makeSimpleTextMessage(PrepareText(newValue, QString()));
|
||||
if (!oldValue.isEmpty()) {
|
||||
auto oldDescription = PrepareText(oldValue, QString());
|
||||
body->addLogEntryOriginal(id, tr::lng_admin_log_previous_description(tr::now), oldDescription);
|
||||
@@ -588,25 +596,16 @@ void GenerateItems(
|
||||
)(tr::now, lt_from, fromLinkText);
|
||||
addSimpleServiceMessage(text);
|
||||
|
||||
auto bodyFlags = Flag::f_entities | Flag::f_from_id;
|
||||
auto bodyClientFlags = MTPDmessage_ClientFlag::f_admin_log_entry;
|
||||
auto bodyReplyTo = 0;
|
||||
auto bodyViaBotId = 0;
|
||||
auto newLink = newValue.isEmpty()
|
||||
? TextWithEntities()
|
||||
: PrepareText(
|
||||
history->session().createInternalLinkFull(newValue),
|
||||
QString());
|
||||
auto body = history->makeMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
bodyFlags,
|
||||
bodyClientFlags,
|
||||
bodyReplyTo,
|
||||
bodyViaBotId,
|
||||
date,
|
||||
peerToUser(from->id),
|
||||
QString(),
|
||||
newLink);
|
||||
auto body = makeSimpleTextMessage(newValue.isEmpty()
|
||||
? TextWithEntities()
|
||||
: PrepareText(
|
||||
history->session().createInternalLinkFull(newValue),
|
||||
QString()));
|
||||
if (!oldValue.isEmpty()) {
|
||||
auto oldLink = PrepareText(
|
||||
history->session().createInternalLinkFull(oldValue),
|
||||
@@ -668,7 +667,7 @@ void GenerateItems(
|
||||
action.vmessage(),
|
||||
history->nextNonHistoryEntryId(),
|
||||
date),
|
||||
MTPDmessage_ClientFlag::f_admin_log_entry,
|
||||
MessageFlag::AdminLogEntry,
|
||||
detachExistingItem),
|
||||
ExtractSentDate(action.vmessage()));
|
||||
}, [&](const auto &) {
|
||||
@@ -697,7 +696,7 @@ void GenerateItems(
|
||||
action.vnew_message(),
|
||||
history->nextNonHistoryEntryId(),
|
||||
date),
|
||||
MTPDmessage_ClientFlag::f_admin_log_entry,
|
||||
MessageFlag::AdminLogEntry,
|
||||
detachExistingItem);
|
||||
if (oldValue.text.isEmpty()) {
|
||||
oldValue = PrepareText(QString(), tr::lng_admin_log_empty_text(tr::now));
|
||||
@@ -723,7 +722,7 @@ void GenerateItems(
|
||||
action.vmessage(),
|
||||
history->nextNonHistoryEntryId(),
|
||||
date),
|
||||
MTPDmessage_ClientFlag::f_admin_log_entry,
|
||||
MessageFlag::AdminLogEntry,
|
||||
detachExistingItem),
|
||||
ExtractSentDate(action.vmessage()));
|
||||
};
|
||||
@@ -743,39 +742,13 @@ void GenerateItems(
|
||||
};
|
||||
|
||||
auto createParticipantInvite = [&](const MTPDchannelAdminLogEventActionParticipantInvite &action) {
|
||||
auto bodyFlags = Flag::f_entities | Flag::f_from_id;
|
||||
auto bodyClientFlags = MTPDmessage_ClientFlag::f_admin_log_entry;
|
||||
auto bodyReplyTo = 0;
|
||||
auto bodyViaBotId = 0;
|
||||
auto bodyText = GenerateParticipantChangeText(channel, action.vparticipant());
|
||||
addPart(history->makeMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
bodyFlags,
|
||||
bodyClientFlags,
|
||||
bodyReplyTo,
|
||||
bodyViaBotId,
|
||||
date,
|
||||
peerToUser(from->id),
|
||||
QString(),
|
||||
bodyText));
|
||||
addSimpleTextMessage(
|
||||
GenerateParticipantChangeText(channel, action.vparticipant()));
|
||||
};
|
||||
|
||||
auto createParticipantToggleBan = [&](const MTPDchannelAdminLogEventActionParticipantToggleBan &action) {
|
||||
auto bodyFlags = Flag::f_entities | Flag::f_from_id;
|
||||
auto bodyClientFlags = MTPDmessage_ClientFlag::f_admin_log_entry;
|
||||
auto bodyReplyTo = 0;
|
||||
auto bodyViaBotId = 0;
|
||||
auto bodyText = GenerateParticipantChangeText(channel, action.vnew_participant(), &action.vprev_participant());
|
||||
addPart(history->makeMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
bodyFlags,
|
||||
bodyClientFlags,
|
||||
bodyReplyTo,
|
||||
bodyViaBotId,
|
||||
date,
|
||||
peerToUser(from->id),
|
||||
QString(),
|
||||
bodyText));
|
||||
addSimpleTextMessage(
|
||||
GenerateParticipantChangeText(channel, action.vnew_participant(), &action.vprev_participant()));
|
||||
};
|
||||
|
||||
auto createParticipantToggleAdmin = [&](const MTPDchannelAdminLogEventActionParticipantToggleAdmin &action) {
|
||||
@@ -785,21 +758,8 @@ void GenerateItems(
|
||||
// the "User > Creator" part and skip the "Creator > Admin" part.
|
||||
return;
|
||||
}
|
||||
auto bodyFlags = Flag::f_entities | Flag::f_from_id;
|
||||
auto bodyClientFlags = MTPDmessage_ClientFlag::f_admin_log_entry;
|
||||
auto bodyReplyTo = 0;
|
||||
auto bodyViaBotId = 0;
|
||||
auto bodyText = GenerateParticipantChangeText(channel, action.vnew_participant(), &action.vprev_participant());
|
||||
addPart(history->makeMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
bodyFlags,
|
||||
bodyClientFlags,
|
||||
bodyReplyTo,
|
||||
bodyViaBotId,
|
||||
date,
|
||||
peerToUser(from->id),
|
||||
QString(),
|
||||
bodyText));
|
||||
addSimpleTextMessage(
|
||||
GenerateParticipantChangeText(channel, action.vnew_participant(), &action.vprev_participant()));
|
||||
};
|
||||
|
||||
auto createChangeStickerSet = [&](const MTPDchannelAdminLogEventActionChangeStickerSet &action) {
|
||||
@@ -825,10 +785,9 @@ void GenerateItems(
|
||||
message.links.push_back(setLink);
|
||||
addPart(history->makeServiceMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
MTPDmessage_ClientFlag::f_admin_log_entry,
|
||||
MessageFlag::AdminLogEntry,
|
||||
date,
|
||||
message,
|
||||
MTPDmessage::Flags(0),
|
||||
peerToUser(from->id)));
|
||||
}
|
||||
};
|
||||
@@ -842,24 +801,11 @@ void GenerateItems(
|
||||
};
|
||||
|
||||
auto createDefaultBannedRights = [&](const MTPDchannelAdminLogEventActionDefaultBannedRights &action) {
|
||||
auto bodyFlags = Flag::f_entities | Flag::f_from_id;
|
||||
auto bodyClientFlags = MTPDmessage_ClientFlag::f_admin_log_entry;
|
||||
auto bodyReplyTo = 0;
|
||||
auto bodyViaBotId = 0;
|
||||
auto bodyText = GenerateDefaultBannedRightsChangeText(
|
||||
channel,
|
||||
ChatRestrictionsInfo(action.vnew_banned_rights()),
|
||||
ChatRestrictionsInfo(action.vprev_banned_rights()));
|
||||
addPart(history->makeMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
bodyFlags,
|
||||
bodyClientFlags,
|
||||
bodyReplyTo,
|
||||
bodyViaBotId,
|
||||
date,
|
||||
peerToUser(from->id),
|
||||
QString(),
|
||||
bodyText));
|
||||
addSimpleTextMessage(
|
||||
GenerateDefaultBannedRightsChangeText(
|
||||
channel,
|
||||
ChatRestrictionsInfo(action.vnew_banned_rights()),
|
||||
ChatRestrictionsInfo(action.vprev_banned_rights())));
|
||||
};
|
||||
|
||||
auto createStopPoll = [&](const MTPDchannelAdminLogEventActionStopPoll &action) {
|
||||
@@ -873,7 +819,7 @@ void GenerateItems(
|
||||
action.vmessage(),
|
||||
history->nextNonHistoryEntryId(),
|
||||
date),
|
||||
MTPDmessage_ClientFlag::f_admin_log_entry,
|
||||
MessageFlag::AdminLogEntry,
|
||||
detachExistingItem),
|
||||
ExtractSentDate(action.vmessage()));
|
||||
};
|
||||
@@ -906,10 +852,9 @@ void GenerateItems(
|
||||
message.links.push_back(chatLink);
|
||||
addPart(history->makeServiceMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
MTPDmessage_ClientFlag::f_admin_log_entry,
|
||||
MessageFlag::AdminLogEntry,
|
||||
date,
|
||||
message,
|
||||
MTPDmessage::Flags(0),
|
||||
peerToUser(from->id)));
|
||||
}
|
||||
};
|
||||
@@ -977,10 +922,9 @@ void GenerateItems(
|
||||
message.links.push_back(link);
|
||||
addPart(history->makeServiceMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
MTPDmessage_ClientFlag::f_admin_log_entry,
|
||||
MessageFlag::AdminLogEntry,
|
||||
date,
|
||||
message,
|
||||
MTPDmessage::Flags(0),
|
||||
peerToUser(from->id)));
|
||||
};
|
||||
|
||||
@@ -1025,10 +969,9 @@ void GenerateItems(
|
||||
}
|
||||
addPart(history->makeServiceMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
MTPDmessage_ClientFlag::f_admin_log_entry,
|
||||
MessageFlag::AdminLogEntry,
|
||||
date,
|
||||
message,
|
||||
MTPDmessage::Flags(0),
|
||||
peerToUser(from->id),
|
||||
nullptr));
|
||||
};
|
||||
@@ -1070,21 +1013,8 @@ void GenerateItems(
|
||||
};
|
||||
|
||||
auto createExportedInviteEdit = [&](const MTPDchannelAdminLogEventActionExportedInviteEdit &data) {
|
||||
auto bodyFlags = Flag::f_entities | Flag::f_from_id;
|
||||
auto bodyClientFlags = MTPDmessage_ClientFlag::f_admin_log_entry;
|
||||
auto bodyReplyTo = 0;
|
||||
auto bodyViaBotId = 0;
|
||||
auto bodyText = GenerateInviteLinkChangeText(data.vnew_invite(), data.vprev_invite());
|
||||
addPart(history->makeMessage(
|
||||
history->nextNonHistoryEntryId(),
|
||||
bodyFlags,
|
||||
bodyClientFlags,
|
||||
bodyReplyTo,
|
||||
bodyViaBotId,
|
||||
date,
|
||||
peerToUser(from->id),
|
||||
QString(),
|
||||
bodyText));
|
||||
addSimpleTextMessage(
|
||||
GenerateInviteLinkChangeText(data.vnew_invite(), data.vprev_invite()));
|
||||
};
|
||||
|
||||
auto createParticipantVolume = [&](const MTPDchannelAdminLogEventActionParticipantVolume &data) {
|
||||
@@ -1113,9 +1043,11 @@ void GenerateItems(
|
||||
const auto wrap = [](int duration) {
|
||||
return (duration == 5)
|
||||
? u"5 seconds"_q
|
||||
: (duration < 3 * 86400)
|
||||
: (duration < 2 * 86400)
|
||||
? tr::lng_manage_messages_ttl_after1(tr::now)
|
||||
: tr::lng_manage_messages_ttl_after2(tr::now);
|
||||
: (duration < 8 * 86400)
|
||||
? tr::lng_manage_messages_ttl_after2(tr::now)
|
||||
: tr::lng_manage_messages_ttl_after3(tr::now);
|
||||
};
|
||||
auto text = !was
|
||||
? tr::lng_admin_log_messages_ttl_set(tr::now, lt_from, fromLinkText, lt_duration, wrap(now))
|
||||
|
||||
@@ -349,7 +349,7 @@ void History::setForwardDraft(MessageIdsList &&items) {
|
||||
|
||||
HistoryItem *History::createItem(
|
||||
const MTPMessage &message,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags localFlags,
|
||||
bool detachExistingItem) {
|
||||
const auto messageId = IdFromMessage(message);
|
||||
if (!messageId) {
|
||||
@@ -362,17 +362,17 @@ HistoryItem *History::createItem(
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return HistoryItem::Create(this, message, clientFlags);
|
||||
return HistoryItem::Create(this, message, localFlags);
|
||||
}
|
||||
|
||||
std::vector<not_null<HistoryItem*>> History::createItems(
|
||||
const QVector<MTPMessage> &data) {
|
||||
auto result = std::vector<not_null<HistoryItem*>>();
|
||||
result.reserve(data.size());
|
||||
const auto clientFlags = MTPDmessage_ClientFlags();
|
||||
const auto localFlags = MessageFlags();
|
||||
for (auto i = data.cend(), e = data.cbegin(); i != e;) {
|
||||
const auto detachExistingItem = true;
|
||||
const auto item = createItem(*--i, clientFlags, detachExistingItem);
|
||||
const auto item = createItem(*--i, localFlags, detachExistingItem);
|
||||
if (item) {
|
||||
result.emplace_back(item);
|
||||
}
|
||||
@@ -382,10 +382,10 @@ std::vector<not_null<HistoryItem*>> History::createItems(
|
||||
|
||||
HistoryItem *History::addNewMessage(
|
||||
const MTPMessage &msg,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags localFlags,
|
||||
NewMessageType type) {
|
||||
const auto detachExistingItem = (type == NewMessageType::Unread);
|
||||
const auto item = createItem(msg, clientFlags, detachExistingItem);
|
||||
const auto item = createItem(msg, localFlags, detachExistingItem);
|
||||
if (!item) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -515,8 +515,35 @@ void History::checkForLoadedAtTop(not_null<HistoryItem*> added) {
|
||||
|
||||
not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
MsgId id,
|
||||
MTPDmessage::Flags flags,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags flags,
|
||||
UserId viaBotId,
|
||||
MsgId replyTo,
|
||||
TimeId date,
|
||||
PeerId from,
|
||||
const QString &postAuthor,
|
||||
const TextWithEntities &text,
|
||||
const MTPMessageMedia &media,
|
||||
const MTPReplyMarkup &markup,
|
||||
uint64 groupedId) {
|
||||
return addNewItem(
|
||||
makeMessage(
|
||||
id,
|
||||
flags,
|
||||
replyTo,
|
||||
viaBotId,
|
||||
date,
|
||||
from,
|
||||
postAuthor,
|
||||
text,
|
||||
media,
|
||||
markup,
|
||||
groupedId),
|
||||
true);
|
||||
}
|
||||
|
||||
not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
MsgId id,
|
||||
MessageFlags flags,
|
||||
TimeId date,
|
||||
PeerId from,
|
||||
const QString &postAuthor,
|
||||
@@ -525,7 +552,6 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
makeMessage(
|
||||
id,
|
||||
flags,
|
||||
clientFlags,
|
||||
date,
|
||||
from,
|
||||
postAuthor,
|
||||
@@ -535,8 +561,7 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
|
||||
not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
MsgId id,
|
||||
MTPDmessage::Flags flags,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags flags,
|
||||
UserId viaBotId,
|
||||
MsgId replyTo,
|
||||
TimeId date,
|
||||
@@ -549,7 +574,6 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
makeMessage(
|
||||
id,
|
||||
flags,
|
||||
clientFlags,
|
||||
replyTo,
|
||||
viaBotId,
|
||||
date,
|
||||
@@ -563,8 +587,7 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
|
||||
not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
MsgId id,
|
||||
MTPDmessage::Flags flags,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags flags,
|
||||
UserId viaBotId,
|
||||
MsgId replyTo,
|
||||
TimeId date,
|
||||
@@ -577,7 +600,6 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
makeMessage(
|
||||
id,
|
||||
flags,
|
||||
clientFlags,
|
||||
replyTo,
|
||||
viaBotId,
|
||||
date,
|
||||
@@ -591,8 +613,7 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
|
||||
not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
MsgId id,
|
||||
MTPDmessage::Flags flags,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags flags,
|
||||
UserId viaBotId,
|
||||
MsgId replyTo,
|
||||
TimeId date,
|
||||
@@ -604,7 +625,6 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
||||
makeMessage(
|
||||
id,
|
||||
flags,
|
||||
clientFlags,
|
||||
replyTo,
|
||||
viaBotId,
|
||||
date,
|
||||
@@ -696,10 +716,10 @@ void History::addUnreadMentionsSlice(const MTPmessages_Messages &result) {
|
||||
|
||||
auto added = false;
|
||||
if (messages) {
|
||||
const auto clientFlags = MTPDmessage_ClientFlags();
|
||||
const auto localFlags = MessageFlags();
|
||||
const auto type = NewMessageType::Existing;
|
||||
for (const auto &message : *messages) {
|
||||
if (const auto item = addNewMessage(message, clientFlags, type)) {
|
||||
if (const auto item = addNewMessage(message, localFlags, type)) {
|
||||
if (item->isUnreadMention()) {
|
||||
_unreadMentions.insert(item->id);
|
||||
added = true;
|
||||
@@ -781,7 +801,7 @@ not_null<HistoryItem*> History::addNewToBack(
|
||||
}
|
||||
if (item->definesReplyKeyboard()) {
|
||||
auto markupFlags = item->replyKeyboardFlags();
|
||||
if (!(markupFlags & MTPDreplyKeyboardMarkup::Flag::f_selective)
|
||||
if (!(markupFlags & ReplyMarkupFlag::Selective)
|
||||
|| item->mentionsMe()) {
|
||||
auto getMarkupSenders = [this]() -> base::flat_set<not_null<PeerData*>>* {
|
||||
if (auto chat = peer->asChat()) {
|
||||
@@ -794,7 +814,8 @@ not_null<HistoryItem*> History::addNewToBack(
|
||||
if (auto markupSenders = getMarkupSenders()) {
|
||||
markupSenders->insert(item->from());
|
||||
}
|
||||
if (markupFlags & MTPDreplyKeyboardMarkup_ClientFlag::f_zero) { // zero markup means replyKeyboardHide
|
||||
if (markupFlags & ReplyMarkupFlag::None) {
|
||||
// None markup means replyKeyboardHide.
|
||||
if (lastKeyboardFrom == item->from()->id
|
||||
|| (!lastKeyboardInited
|
||||
&& !peer->isChat()
|
||||
@@ -1294,13 +1315,13 @@ void History::addItemsToLists(
|
||||
if (item->author()->id) {
|
||||
if (markupSenders) { // chats with bots
|
||||
if (!lastKeyboardInited && item->definesReplyKeyboard() && !item->out()) {
|
||||
auto markupFlags = item->replyKeyboardFlags();
|
||||
if (!(markupFlags & MTPDreplyKeyboardMarkup::Flag::f_selective) || item->mentionsMe()) {
|
||||
const auto markupFlags = item->replyKeyboardFlags();
|
||||
if (!(markupFlags & ReplyMarkupFlag::Selective) || item->mentionsMe()) {
|
||||
bool wasKeyboardHide = markupSenders->contains(item->author());
|
||||
if (!wasKeyboardHide) {
|
||||
markupSenders->insert(item->author());
|
||||
}
|
||||
if (!(markupFlags & MTPDreplyKeyboardMarkup_ClientFlag::f_zero)) {
|
||||
if (!(markupFlags & ReplyMarkupFlag::None)) {
|
||||
if (!lastKeyboardInited) {
|
||||
bool botNotInChat = false;
|
||||
if (peer->isChat()) {
|
||||
@@ -1321,9 +1342,9 @@ void History::addItemsToLists(
|
||||
}
|
||||
}
|
||||
} else if (!lastKeyboardInited && item->definesReplyKeyboard() && !item->out()) { // conversations with bots
|
||||
MTPDreplyKeyboardMarkup::Flags markupFlags = item->replyKeyboardFlags();
|
||||
if (!(markupFlags & MTPDreplyKeyboardMarkup::Flag::f_selective) || item->mentionsMe()) {
|
||||
if (markupFlags & MTPDreplyKeyboardMarkup_ClientFlag::f_zero) {
|
||||
const auto markupFlags = item->replyKeyboardFlags();
|
||||
if (!(markupFlags & ReplyMarkupFlag::Selective) || item->mentionsMe()) {
|
||||
if (markupFlags & ReplyMarkupFlag::None) {
|
||||
clearLastKeyboard();
|
||||
} else {
|
||||
lastKeyboardInited = true;
|
||||
@@ -2351,7 +2372,7 @@ void History::setFakeChatListMessageFrom(const MTPmessages_Messages &data) {
|
||||
}
|
||||
const auto item = owner().addNewMessage(
|
||||
*other,
|
||||
MTPDmessage_ClientFlags(),
|
||||
MessageFlags(),
|
||||
NewMessageType::Existing);
|
||||
if (!item || item->isGroupMigrate()) {
|
||||
// Not better than the last one.
|
||||
@@ -2784,9 +2805,8 @@ HistoryService *History::insertJoinedMessage() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto flags = MTPDmessage::Flags();
|
||||
const auto inviteDate = peer->asChannel()->inviteDate;
|
||||
_joinedMessage = GenerateJoinedMessage(this, inviteDate, inviter, flags);
|
||||
_joinedMessage = GenerateJoinedMessage(this, inviteDate, inviter);
|
||||
insertLocalMessage(_joinedMessage);
|
||||
return _joinedMessage;
|
||||
}
|
||||
|
||||
@@ -118,23 +118,33 @@ public:
|
||||
|
||||
HistoryItem *addNewMessage(
|
||||
const MTPMessage &msg,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags localFlags,
|
||||
NewMessageType type);
|
||||
HistoryItem *addToHistory(
|
||||
const MTPMessage &msg,
|
||||
MTPDmessage_ClientFlags clientFlags);
|
||||
MessageFlags localFlags);
|
||||
not_null<HistoryItem*> addNewLocalMessage(
|
||||
MsgId id,
|
||||
MTPDmessage::Flags flags,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags flags,
|
||||
UserId viaBotId,
|
||||
MsgId replyTo,
|
||||
TimeId date,
|
||||
PeerId from,
|
||||
const QString &postAuthor,
|
||||
const TextWithEntities &text,
|
||||
const MTPMessageMedia &media,
|
||||
const MTPReplyMarkup &markup,
|
||||
uint64 groupedId = 0);
|
||||
not_null<HistoryItem*> addNewLocalMessage(
|
||||
MsgId id,
|
||||
MessageFlags flags,
|
||||
TimeId date,
|
||||
PeerId from,
|
||||
const QString &postAuthor,
|
||||
not_null<HistoryMessage*> forwardOriginal);
|
||||
not_null<HistoryItem*> addNewLocalMessage(
|
||||
MsgId id,
|
||||
MTPDmessage::Flags flags,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags flags,
|
||||
UserId viaBotId,
|
||||
MsgId replyTo,
|
||||
TimeId date,
|
||||
@@ -145,8 +155,7 @@ public:
|
||||
const MTPReplyMarkup &markup);
|
||||
not_null<HistoryItem*> addNewLocalMessage(
|
||||
MsgId id,
|
||||
MTPDmessage::Flags flags,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags flags,
|
||||
UserId viaBotId,
|
||||
MsgId replyTo,
|
||||
TimeId date,
|
||||
@@ -157,8 +166,7 @@ public:
|
||||
const MTPReplyMarkup &markup);
|
||||
not_null<HistoryItem*> addNewLocalMessage(
|
||||
MsgId id,
|
||||
MTPDmessage::Flags flags,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags flags,
|
||||
UserId viaBotId,
|
||||
MsgId replyTo,
|
||||
TimeId date,
|
||||
@@ -170,7 +178,7 @@ public:
|
||||
// Used only internally and for channel admin log.
|
||||
HistoryItem *createItem(
|
||||
const MTPMessage &message,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags localFlags,
|
||||
bool detachExistingItem);
|
||||
std::vector<not_null<HistoryItem*>> createItems(
|
||||
const QVector<MTPMessage> &data);
|
||||
|
||||
@@ -46,7 +46,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/unixtime.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "layout.h"
|
||||
#include "layout/layout_selection.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "core/application.h"
|
||||
@@ -2620,19 +2620,7 @@ bool HistoryInner::elementIsGifPaused() {
|
||||
void HistoryInner::elementSendBotCommand(
|
||||
const QString &command,
|
||||
const FullMsgId &context) {
|
||||
if (auto peer = Ui::getPeerForMouseAction()) { // old way
|
||||
auto bot = peer->isUser() ? peer->asUser() : nullptr;
|
||||
if (!bot) {
|
||||
if (const auto view = App::hoveredLinkItem()) {
|
||||
// may return nullptr
|
||||
bot = view->data()->fromOriginal()->asUser();
|
||||
}
|
||||
}
|
||||
Ui::showPeerHistory(peer, ShowAtTheEndMsgId);
|
||||
App::sendBotCommand(peer, bot, command);
|
||||
} else {
|
||||
App::insertBotCommand(command);
|
||||
}
|
||||
_widget->sendBotCommand({ _history->peer, command, context });
|
||||
}
|
||||
|
||||
void HistoryInner::elementHandleViaClick(not_null<UserData*> bot) {
|
||||
@@ -2647,6 +2635,10 @@ not_null<Ui::PathShiftGradient*> HistoryInner::elementPathShiftGradient() {
|
||||
return _pathGradient.get();
|
||||
}
|
||||
|
||||
void HistoryInner::elementReplyTo(const FullMsgId &to) {
|
||||
return _widget->replyToMessage(to);
|
||||
}
|
||||
|
||||
auto HistoryInner::getSelectionState() const
|
||||
-> HistoryView::TopBarWidget::SelectedState {
|
||||
auto result = HistoryView::TopBarWidget::SelectedState {};
|
||||
@@ -3550,6 +3542,11 @@ not_null<HistoryView::ElementDelegate*> HistoryInner::ElementDelegate() {
|
||||
|
||||
return Instance->elementPathShiftGradient();
|
||||
}
|
||||
void elementReplyTo(const FullMsgId &to) override {
|
||||
if (Instance) {
|
||||
Instance->elementReplyTo(to);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static Result result;
|
||||
|
||||
@@ -109,6 +109,7 @@ public:
|
||||
void elementHandleViaClick(not_null<UserData*> bot);
|
||||
bool elementIsChatWide();
|
||||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient();
|
||||
void elementReplyTo(const FullMsgId &to);
|
||||
|
||||
void updateBotInfo(bool recount = true);
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "layout.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "history/history_item_components.h"
|
||||
@@ -60,8 +59,7 @@ enum class MediaCheckResult {
|
||||
not_null<HistoryItem*> CreateUnsupportedMessage(
|
||||
not_null<History*> history,
|
||||
MsgId msgId,
|
||||
MTPDmessage::Flags flags,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags flags,
|
||||
MsgId replyTo,
|
||||
UserId viaBotId,
|
||||
TimeId date,
|
||||
@@ -73,18 +71,21 @@ not_null<HistoryItem*> CreateUnsupportedMessage(
|
||||
TextUtilities::ParseEntities(text, Ui::ItemTextNoMonoOptions().flags);
|
||||
text.entities.push_front(
|
||||
EntityInText(EntityType::Italic, 0, text.text.size()));
|
||||
flags &= ~MTPDmessage::Flag::f_post_author;
|
||||
flags |= MTPDmessage::Flag::f_legacy;
|
||||
flags &= ~MessageFlag::HasPostAuthor;
|
||||
flags |= MessageFlag::Legacy;
|
||||
const auto groupedId = uint64();
|
||||
return history->makeMessage(
|
||||
msgId,
|
||||
flags,
|
||||
clientFlags,
|
||||
replyTo,
|
||||
viaBotId,
|
||||
date,
|
||||
from,
|
||||
QString(),
|
||||
text);
|
||||
text,
|
||||
MTP_messageMediaEmpty(),
|
||||
MTPReplyMarkup(),
|
||||
groupedId);
|
||||
}
|
||||
|
||||
MediaCheckResult CheckMessageMedia(const MTPMessageMedia &media) {
|
||||
@@ -171,15 +172,13 @@ void HistoryItem::HistoryItem::Destroyer::operator()(HistoryItem *value) {
|
||||
HistoryItem::HistoryItem(
|
||||
not_null<History*> history,
|
||||
MsgId id,
|
||||
MTPDmessage::Flags flags,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags flags,
|
||||
TimeId date,
|
||||
PeerId from)
|
||||
: id(id)
|
||||
, _history(history)
|
||||
, _from(from ? history->owner().peer(from) : history->peer)
|
||||
, _flags(flags)
|
||||
, _clientFlags(clientFlags)
|
||||
, _date(date) {
|
||||
if (isHistoryEntry() && IsClientMsgId(id)) {
|
||||
_history->registerLocalMessage(this);
|
||||
@@ -232,7 +231,7 @@ void HistoryItem::setGroupId(MessageGroupId groupId) {
|
||||
|
||||
HistoryMessageReplyMarkup *HistoryItem::inlineReplyMarkup() {
|
||||
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
|
||||
if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_inline) {
|
||||
if (markup->flags & ReplyMarkupFlag::Inline) {
|
||||
return markup;
|
||||
}
|
||||
}
|
||||
@@ -319,11 +318,11 @@ bool HistoryItem::hasUnreadMediaFlag() const {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return _flags & MTPDmessage::Flag::f_media_unread;
|
||||
return _flags & MessageFlag::MediaIsUnread;
|
||||
}
|
||||
|
||||
bool HistoryItem::isUnreadMention() const {
|
||||
return mentionsMe() && (_flags & MTPDmessage::Flag::f_media_unread);
|
||||
return mentionsMe() && (_flags & MessageFlag::MediaIsUnread);
|
||||
}
|
||||
|
||||
bool HistoryItem::mentionsMe() const {
|
||||
@@ -331,7 +330,7 @@ bool HistoryItem::mentionsMe() const {
|
||||
&& !Core::App().settings().notifyAboutPinned()) {
|
||||
return false;
|
||||
}
|
||||
return _flags & MTPDmessage::Flag::f_mentioned;
|
||||
return _flags & MessageFlag::MentionsMe;
|
||||
}
|
||||
|
||||
bool HistoryItem::isUnreadMedia() const {
|
||||
@@ -348,7 +347,7 @@ bool HistoryItem::isUnreadMedia() const {
|
||||
}
|
||||
|
||||
void HistoryItem::markMediaRead() {
|
||||
_flags &= ~MTPDmessage::Flag::f_media_unread;
|
||||
_flags &= ~MessageFlag::MediaIsUnread;
|
||||
|
||||
if (mentionsMe()) {
|
||||
history()->updateChatListEntry();
|
||||
@@ -359,7 +358,7 @@ void HistoryItem::markMediaRead() {
|
||||
void HistoryItem::setIsPinned(bool pinned) {
|
||||
const auto changed = (isPinned() != pinned);
|
||||
if (pinned) {
|
||||
_flags |= MTPDmessage::Flag::f_pinned;
|
||||
_flags |= MessageFlag::Pinned;
|
||||
history()->session().storage().add(Storage::SharedMediaAddExisting(
|
||||
history()->peer->id,
|
||||
Storage::SharedMediaType::Pinned,
|
||||
@@ -367,7 +366,7 @@ void HistoryItem::setIsPinned(bool pinned) {
|
||||
{ id, id }));
|
||||
history()->peer->setHasPinnedMessages(true);
|
||||
} else {
|
||||
_flags &= ~MTPDmessage::Flag::f_pinned;
|
||||
_flags &= ~MessageFlag::Pinned;
|
||||
history()->session().storage().remove(Storage::SharedMediaRemoveOne(
|
||||
history()->peer->id,
|
||||
Storage::SharedMediaType::Pinned,
|
||||
@@ -380,7 +379,7 @@ void HistoryItem::setIsPinned(bool pinned) {
|
||||
|
||||
bool HistoryItem::definesReplyKeyboard() const {
|
||||
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
|
||||
if (markup->flags & MTPDreplyKeyboardMarkup_ClientFlag::f_inline) {
|
||||
if (markup->flags & ReplyMarkupFlag::Inline) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -388,10 +387,10 @@ bool HistoryItem::definesReplyKeyboard() const {
|
||||
|
||||
// optimization: don't create markup component for the case
|
||||
// MTPDreplyKeyboardHide with flags = 0, assume it has f_zero flag
|
||||
return (_flags & MTPDmessage::Flag::f_reply_markup);
|
||||
return (_flags & MessageFlag::HasReplyMarkup);
|
||||
}
|
||||
|
||||
MTPDreplyKeyboardMarkup::Flags HistoryItem::replyKeyboardFlags() const {
|
||||
ReplyMarkupFlags HistoryItem::replyKeyboardFlags() const {
|
||||
Expects(definesReplyKeyboard());
|
||||
|
||||
if (const auto markup = Get<HistoryMessageReplyMarkup>()) {
|
||||
@@ -400,7 +399,7 @@ MTPDreplyKeyboardMarkup::Flags HistoryItem::replyKeyboardFlags() const {
|
||||
|
||||
// optimization: don't create markup component for the case
|
||||
// MTPDreplyKeyboardHide with flags = 0, assume it has f_zero flag
|
||||
return MTPDreplyKeyboardMarkup_ClientFlag::f_zero | 0;
|
||||
return ReplyMarkupFlag::None;
|
||||
}
|
||||
|
||||
void HistoryItem::addLogEntryOriginal(
|
||||
@@ -442,22 +441,22 @@ UserData *HistoryItem::getMessageBot() const {
|
||||
|
||||
bool HistoryItem::isHistoryEntry() const {
|
||||
return IsServerMsgId(id)
|
||||
|| (_clientFlags & MTPDmessage_ClientFlag::f_local_history_entry);
|
||||
|| (_flags & MessageFlag::LocalHistoryEntry);
|
||||
}
|
||||
|
||||
bool HistoryItem::isAdminLogEntry() const {
|
||||
return (_clientFlags & MTPDmessage_ClientFlag::f_admin_log_entry);
|
||||
return (_flags & MessageFlag::AdminLogEntry);
|
||||
}
|
||||
|
||||
bool HistoryItem::isFromScheduled() const {
|
||||
return isHistoryEntry()
|
||||
&& (_flags & MTPDmessage::Flag::f_from_scheduled);
|
||||
&& (_flags & MessageFlag::IsOrWasScheduled);
|
||||
}
|
||||
|
||||
bool HistoryItem::isScheduled() const {
|
||||
return !isHistoryEntry()
|
||||
&& !isAdminLogEntry()
|
||||
&& (_flags & MTPDmessage::Flag::f_from_scheduled);
|
||||
&& (_flags & MessageFlag::IsOrWasScheduled);
|
||||
}
|
||||
|
||||
void HistoryItem::destroy() {
|
||||
@@ -561,11 +560,11 @@ void HistoryItem::indexAsNewItem() {
|
||||
}
|
||||
|
||||
void HistoryItem::setRealId(MsgId newId) {
|
||||
Expects(_clientFlags & MTPDmessage_ClientFlag::f_sending);
|
||||
Expects(_flags & MessageFlag::BeingSent);
|
||||
Expects(IsClientMsgId(id));
|
||||
|
||||
const auto oldId = std::exchange(id, newId);
|
||||
_clientFlags &= ~MTPDmessage_ClientFlag::f_sending;
|
||||
_flags &= ~MessageFlag::BeingSent;
|
||||
if (IsServerMsgId(id)) {
|
||||
_history->unregisterLocalMessage(this);
|
||||
}
|
||||
@@ -847,11 +846,10 @@ void HistoryItem::applyTTL(TimeId destroyAt) {
|
||||
}
|
||||
|
||||
void HistoryItem::sendFailed() {
|
||||
Expects(_clientFlags & MTPDmessage_ClientFlag::f_sending);
|
||||
Expects(!(_clientFlags & MTPDmessage_ClientFlag::f_failed));
|
||||
Expects(_flags & MessageFlag::BeingSent);
|
||||
Expects(!(_flags & MessageFlag::SendingFailed));
|
||||
|
||||
_clientFlags = (_clientFlags | MTPDmessage_ClientFlag::f_failed)
|
||||
& ~MTPDmessage_ClientFlag::f_sending;
|
||||
_flags = (_flags | MessageFlag::SendingFailed) & ~MessageFlag::BeingSent;
|
||||
history()->session().changes().historyUpdated(
|
||||
history(),
|
||||
Data::HistoryUpdate::Flag::LocalMessages);
|
||||
@@ -896,7 +894,7 @@ bool HistoryItem::unread() const {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return (_clientFlags & MTPDmessage_ClientFlag::f_clientside_unread);
|
||||
return (_flags & MessageFlag::ClientSideUnread);
|
||||
}
|
||||
|
||||
bool HistoryItem::showNotification() const {
|
||||
@@ -910,7 +908,7 @@ bool HistoryItem::showNotification() const {
|
||||
}
|
||||
|
||||
void HistoryItem::markClientSideAsRead() {
|
||||
_clientFlags &= ~MTPDmessage_ClientFlag::f_clientside_unread;
|
||||
_flags &= ~MessageFlag::ClientSideUnread;
|
||||
}
|
||||
|
||||
MessageGroupId HistoryItem::groupId() const {
|
||||
@@ -1045,10 +1043,44 @@ ClickHandlerPtr goToMessageClickHandler(
|
||||
});
|
||||
}
|
||||
|
||||
MessageFlags FlagsFromMTP(MTPDmessage::Flags flags) {
|
||||
using Flag = MessageFlag;
|
||||
using MTP = MTPDmessage::Flag;
|
||||
return Flag()
|
||||
| ((flags & MTP::f_out) ? Flag::Outgoing : Flag())
|
||||
| ((flags & MTP::f_mentioned) ? Flag::MentionsMe : Flag())
|
||||
| ((flags & MTP::f_media_unread) ? Flag::MediaIsUnread : Flag())
|
||||
| ((flags & MTP::f_silent) ? Flag::Silent : Flag())
|
||||
| ((flags & MTP::f_post) ? Flag::Post : Flag())
|
||||
| ((flags & MTP::f_legacy) ? Flag::Legacy : Flag())
|
||||
| ((flags & MTP::f_edit_hide) ? Flag::HideEdited : Flag())
|
||||
| ((flags & MTP::f_pinned) ? Flag::Pinned : Flag())
|
||||
| ((flags & MTP::f_from_id) ? Flag::HasFromId : Flag())
|
||||
| ((flags & MTP::f_via_bot_id) ? Flag::HasViaBot : Flag())
|
||||
| ((flags & MTP::f_reply_to) ? Flag::HasReplyInfo : Flag())
|
||||
| ((flags & MTP::f_reply_markup) ? Flag::HasReplyMarkup : Flag())
|
||||
| ((flags & MTP::f_from_scheduled) ? Flag::IsOrWasScheduled : Flag())
|
||||
| ((flags & MTP::f_views) ? Flag::HasViews : Flag());
|
||||
}
|
||||
|
||||
MessageFlags FlagsFromMTP(MTPDmessageService::Flags flags) {
|
||||
using Flag = MessageFlag;
|
||||
using MTP = MTPDmessageService::Flag;
|
||||
return Flag()
|
||||
| ((flags & MTP::f_out) ? Flag::Outgoing : Flag())
|
||||
| ((flags & MTP::f_mentioned) ? Flag::MentionsMe : Flag())
|
||||
| ((flags & MTP::f_media_unread) ? Flag::MediaIsUnread : Flag())
|
||||
| ((flags & MTP::f_silent) ? Flag::Silent : Flag())
|
||||
| ((flags & MTP::f_post) ? Flag::Post : Flag())
|
||||
| ((flags & MTP::f_legacy) ? Flag::Legacy : Flag())
|
||||
| ((flags & MTP::f_from_id) ? Flag::HasFromId : Flag())
|
||||
| ((flags & MTP::f_reply_to) ? Flag::HasReplyInfo : Flag());
|
||||
}
|
||||
|
||||
not_null<HistoryItem*> HistoryItem::Create(
|
||||
not_null<History*> history,
|
||||
const MTPMessage &message,
|
||||
MTPDmessage_ClientFlags clientFlags) {
|
||||
MessageFlags localFlags) {
|
||||
return message.match([&](const MTPDmessage &data) -> HistoryItem* {
|
||||
const auto media = data.vmedia();
|
||||
const auto checked = media
|
||||
@@ -1058,8 +1090,7 @@ not_null<HistoryItem*> HistoryItem::Create(
|
||||
return CreateUnsupportedMessage(
|
||||
history,
|
||||
data.vid().v,
|
||||
data.vflags().v,
|
||||
clientFlags,
|
||||
FlagsFromMTP(data.vflags().v) | localFlags,
|
||||
MsgId(0), // No need to pass reply_to data here.
|
||||
data.vvia_bot_id().value_or_empty(),
|
||||
data.vdate().v,
|
||||
@@ -1070,27 +1101,26 @@ not_null<HistoryItem*> HistoryItem::Create(
|
||||
};
|
||||
return history->makeServiceMessage(
|
||||
data.vid().v,
|
||||
clientFlags,
|
||||
FlagsFromMTP(data.vflags().v) | localFlags,
|
||||
data.vdate().v,
|
||||
text,
|
||||
data.vflags().v,
|
||||
data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0));
|
||||
} else if (checked == MediaCheckResult::HasTimeToLive) {
|
||||
return history->makeServiceMessage(data, clientFlags);
|
||||
return history->makeServiceMessage(data, localFlags);
|
||||
}
|
||||
return history->makeMessage(data, clientFlags);
|
||||
return history->makeMessage(data, localFlags);
|
||||
}, [&](const MTPDmessageService &data) -> HistoryItem* {
|
||||
if (data.vaction().type() == mtpc_messageActionPhoneCall) {
|
||||
return history->makeMessage(data, clientFlags);
|
||||
return history->makeMessage(data, localFlags);
|
||||
}
|
||||
return history->makeServiceMessage(data, clientFlags);
|
||||
return history->makeServiceMessage(data, localFlags);
|
||||
}, [&](const MTPDmessageEmpty &data) -> HistoryItem* {
|
||||
const auto text = HistoryService::PreparedText{
|
||||
tr::lng_message_empty(tr::now)
|
||||
};
|
||||
return history->makeServiceMessage(
|
||||
data.vid().v,
|
||||
clientFlags,
|
||||
localFlags,
|
||||
TimeId(0),
|
||||
text);
|
||||
});
|
||||
|
||||
@@ -57,12 +57,27 @@ class ElementDelegate;
|
||||
struct HiddenSenderInfo;
|
||||
class History;
|
||||
|
||||
enum class ReplyMarkupFlag : uint32 {
|
||||
None = (1U << 0),
|
||||
ForceReply = (1U << 1),
|
||||
HasSwitchInlineButton = (1U << 2),
|
||||
Inline = (1U << 3),
|
||||
Resize = (1U << 4),
|
||||
SingleUse = (1U << 5),
|
||||
Selective = (1U << 6),
|
||||
};
|
||||
inline constexpr bool is_flag_type(ReplyMarkupFlag) { return true; }
|
||||
using ReplyMarkupFlags = base::flags<ReplyMarkupFlag>;
|
||||
|
||||
[[nodiscard]] MessageFlags FlagsFromMTP(MTPDmessage::Flags flags);
|
||||
[[nodiscard]] MessageFlags FlagsFromMTP(MTPDmessageService::Flags flags);
|
||||
|
||||
class HistoryItem : public RuntimeComposer<HistoryItem> {
|
||||
public:
|
||||
static not_null<HistoryItem*> Create(
|
||||
not_null<History*> history,
|
||||
const MTPMessage &message,
|
||||
MTPDmessage_ClientFlags clientFlags);
|
||||
MessageFlags localFlags);
|
||||
|
||||
struct Destroyer {
|
||||
void operator()(HistoryItem *value);
|
||||
@@ -113,10 +128,10 @@ public:
|
||||
|
||||
void destroy();
|
||||
[[nodiscard]] bool out() const {
|
||||
return _flags & MTPDmessage::Flag::f_out;
|
||||
return _flags & MessageFlag::Outgoing;
|
||||
}
|
||||
[[nodiscard]] bool isPinned() const {
|
||||
return _flags & MTPDmessage::Flag::f_pinned;
|
||||
return _flags & MessageFlag::Pinned;
|
||||
}
|
||||
[[nodiscard]] bool unread() const;
|
||||
[[nodiscard]] bool showNotification() const;
|
||||
@@ -149,47 +164,47 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]] bool definesReplyKeyboard() const;
|
||||
[[nodiscard]] MTPDreplyKeyboardMarkup::Flags replyKeyboardFlags() const;
|
||||
[[nodiscard]] ReplyMarkupFlags replyKeyboardFlags() const;
|
||||
|
||||
[[nodiscard]] bool hasSwitchInlineButton() const {
|
||||
return _clientFlags & MTPDmessage_ClientFlag::f_has_switch_inline_button;
|
||||
return _flags & MessageFlag::HasSwitchInlineButton;
|
||||
}
|
||||
[[nodiscard]] bool hasTextLinks() const {
|
||||
return _clientFlags & MTPDmessage_ClientFlag::f_has_text_links;
|
||||
return _flags & MessageFlag::HasTextLinks;
|
||||
}
|
||||
[[nodiscard]] bool isGroupEssential() const {
|
||||
return _clientFlags & MTPDmessage_ClientFlag::f_is_group_essential;
|
||||
return _flags & MessageFlag::IsGroupEssential;
|
||||
}
|
||||
[[nodiscard]] bool isLocalUpdateMedia() const {
|
||||
return _clientFlags & MTPDmessage_ClientFlag::f_is_local_update_media;
|
||||
return _flags & MessageFlag::IsLocalUpdateMedia;
|
||||
}
|
||||
void setIsLocalUpdateMedia(bool flag) {
|
||||
if (flag) {
|
||||
_clientFlags |= MTPDmessage_ClientFlag::f_is_local_update_media;
|
||||
_flags |= MessageFlag::IsLocalUpdateMedia;
|
||||
} else {
|
||||
_clientFlags &= ~MTPDmessage_ClientFlag::f_is_local_update_media;
|
||||
_flags &= ~MessageFlag::IsLocalUpdateMedia;
|
||||
}
|
||||
}
|
||||
[[nodiscard]] bool isGroupMigrate() const {
|
||||
return isGroupEssential() && isEmpty();
|
||||
}
|
||||
[[nodiscard]] bool isIsolatedEmoji() const {
|
||||
return _clientFlags & MTPDmessage_ClientFlag::f_isolated_emoji;
|
||||
return _flags & MessageFlag::IsolatedEmoji;
|
||||
}
|
||||
[[nodiscard]] bool hasViews() const {
|
||||
return _flags & MTPDmessage::Flag::f_views;
|
||||
return _flags & MessageFlag::HasViews;
|
||||
}
|
||||
[[nodiscard]] bool isPost() const {
|
||||
return _flags & MTPDmessage::Flag::f_post;
|
||||
return _flags & MessageFlag::Post;
|
||||
}
|
||||
[[nodiscard]] bool isSilent() const {
|
||||
return _flags & MTPDmessage::Flag::f_silent;
|
||||
return _flags & MessageFlag::Silent;
|
||||
}
|
||||
[[nodiscard]] bool isSending() const {
|
||||
return _clientFlags & MTPDmessage_ClientFlag::f_sending;
|
||||
return _flags & MessageFlag::BeingSent;
|
||||
}
|
||||
[[nodiscard]] bool hasFailed() const {
|
||||
return _clientFlags & MTPDmessage_ClientFlag::f_failed;
|
||||
return _flags & MessageFlag::SendingFailed;
|
||||
}
|
||||
void sendFailed();
|
||||
[[nodiscard]] virtual int viewsCount() const {
|
||||
@@ -410,8 +425,7 @@ protected:
|
||||
HistoryItem(
|
||||
not_null<History*> history,
|
||||
MsgId id,
|
||||
MTPDmessage::Flags flags,
|
||||
MTPDmessage_ClientFlags clientFlags,
|
||||
MessageFlags flags,
|
||||
TimeId date,
|
||||
PeerId from);
|
||||
|
||||
@@ -424,8 +438,7 @@ protected:
|
||||
|
||||
const not_null<History*> _history;
|
||||
not_null<PeerData*> _from;
|
||||
MTPDmessage::Flags _flags = 0;
|
||||
MTPDmessage_ClientFlags _clientFlags = 0;
|
||||
MessageFlags _flags = 0;
|
||||
|
||||
void invalidateChatListEntry();
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "history/view/media/history_view_document.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "layout/layout_utils.h"
|
||||
#include "layout/layout_position.h"
|
||||
#include "mainwindow.h"
|
||||
#include "media/audio/media_audio.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
@@ -447,9 +447,13 @@ auto ReplyMarkupClickHandler::getUrlButton() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ReplyMarkupClickHandler::onClickImpl() const {
|
||||
void ReplyMarkupClickHandler::onClick(ClickContext context) const {
|
||||
if (context.button != Qt::LeftButton) {
|
||||
return;
|
||||
}
|
||||
if (const auto item = _owner->message(_itemId)) {
|
||||
App::activateBotCommand(item, _row, _column);
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
App::activateBotCommand(my.sessionWindow.get(), item, _row, _column);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,7 +887,7 @@ void HistoryMessageReplyMarkup::createFromButtonRows(
|
||||
if (type == Type::SwitchInline) {
|
||||
// Optimization flag.
|
||||
// Fast check on all new messages if there is a switch button to auto-click it.
|
||||
flags |= MTPDreplyKeyboardMarkup_ClientFlag::f_has_switch_inline_button;
|
||||
flags |= ReplyMarkupFlag::HasSwitchInlineButton;
|
||||
}
|
||||
}, [&](const MTPDkeyboardButtonGame &data) {
|
||||
row.emplace_back(Type::Game, qs(data.vtext()));
|
||||
@@ -928,35 +932,26 @@ void HistoryMessageReplyMarkup::create(const MTPReplyMarkup &markup) {
|
||||
rows.clear();
|
||||
inlineKeyboard = nullptr;
|
||||
|
||||
switch (markup.type()) {
|
||||
case mtpc_replyKeyboardMarkup: {
|
||||
auto &d = markup.c_replyKeyboardMarkup();
|
||||
flags = d.vflags().v;
|
||||
placeholder = d.vplaceholder() ? qs(*d.vplaceholder()) : QString();
|
||||
|
||||
createFromButtonRows(d.vrows().v);
|
||||
} break;
|
||||
|
||||
case mtpc_replyInlineMarkup: {
|
||||
auto &d = markup.c_replyInlineMarkup();
|
||||
flags = MTPDreplyKeyboardMarkup::Flags(0) | MTPDreplyKeyboardMarkup_ClientFlag::f_inline;
|
||||
using Flag = ReplyMarkupFlag;
|
||||
markup.match([&](const MTPDreplyKeyboardMarkup &data) {
|
||||
flags = (data.is_resize() ? Flag::Resize : Flag())
|
||||
| (data.is_selective() ? Flag::Selective : Flag())
|
||||
| (data.is_single_use() ? Flag::SingleUse : Flag());
|
||||
placeholder = qs(data.vplaceholder().value_or_empty());
|
||||
createFromButtonRows(data.vrows().v);
|
||||
}, [&](const MTPDreplyInlineMarkup &data) {
|
||||
flags = Flag::Inline;
|
||||
placeholder = QString();
|
||||
|
||||
createFromButtonRows(d.vrows().v);
|
||||
} break;
|
||||
|
||||
case mtpc_replyKeyboardHide: {
|
||||
auto &d = markup.c_replyKeyboardHide();
|
||||
flags = mtpCastFlags(d.vflags()) | MTPDreplyKeyboardMarkup_ClientFlag::f_zero;
|
||||
createFromButtonRows(data.vrows().v);
|
||||
}, [&](const MTPDreplyKeyboardHide &data) {
|
||||
flags = Flag::None | (data.is_selective() ? Flag::Selective : Flag());
|
||||
placeholder = QString();
|
||||
} break;
|
||||
|
||||
case mtpc_replyKeyboardForceReply: {
|
||||
auto &d = markup.c_replyKeyboardForceReply();
|
||||
flags = mtpCastFlags(d.vflags()) | MTPDreplyKeyboardMarkup_ClientFlag::f_force_reply;
|
||||
placeholder = d.vplaceholder() ? qs(*d.vplaceholder()) : QString();
|
||||
} break;
|
||||
}
|
||||
}, [&](const MTPDreplyKeyboardForceReply &data) {
|
||||
flags = Flag::ForceReply
|
||||
| (data.is_selective() ? Flag::Selective : Flag())
|
||||
| (data.is_single_use() ? Flag::SingleUse : Flag());
|
||||
placeholder = qs(data.vplaceholder().value_or_empty());
|
||||
});
|
||||
}
|
||||
|
||||
void HistoryMessageReplyMarkup::create(
|
||||
|
||||
@@ -225,18 +225,19 @@ struct HistoryMessageMarkupButton {
|
||||
|
||||
};
|
||||
|
||||
struct HistoryMessageReplyMarkup : public RuntimeComponent<HistoryMessageReplyMarkup, HistoryItem> {
|
||||
struct HistoryMessageReplyMarkup
|
||||
: public RuntimeComponent<HistoryMessageReplyMarkup, HistoryItem> {
|
||||
using Button = HistoryMessageMarkupButton;
|
||||
|
||||
HistoryMessageReplyMarkup() = default;
|
||||
HistoryMessageReplyMarkup(MTPDreplyKeyboardMarkup::Flags f) : flags(f) {
|
||||
HistoryMessageReplyMarkup(ReplyMarkupFlags flags) : flags(flags) {
|
||||
}
|
||||
|
||||
void create(const MTPReplyMarkup &markup);
|
||||
void create(const HistoryMessageReplyMarkup &markup);
|
||||
|
||||
std::vector<std::vector<Button>> rows;
|
||||
MTPDreplyKeyboardMarkup::Flags flags = 0;
|
||||
ReplyMarkupFlags flags = 0;
|
||||
QString placeholder;
|
||||
|
||||
std::unique_ptr<ReplyKeyboard> inlineKeyboard;
|
||||
@@ -246,7 +247,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class ReplyMarkupClickHandler : public LeftButtonClickHandler {
|
||||
class ReplyMarkupClickHandler : public ClickHandler {
|
||||
public:
|
||||
ReplyMarkupClickHandler(
|
||||
not_null<Data::Session*> owner,
|
||||
@@ -278,8 +279,7 @@ public:
|
||||
_itemId = msgId;
|
||||
}
|
||||
|
||||
protected:
|
||||
void onClickImpl() const override;
|
||||
void onClick(ClickContext context) const override;
|
||||
|
||||
private:
|
||||
const not_null<Data::Session*> _owner;
|
||||
|
||||