diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml
index f0f9e1d3e..25d6f2fd7 100644
--- a/.github/workflows/win.yml
+++ b/.github/workflows/win.yml
@@ -59,28 +59,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: "true"
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
@@ -91,6 +97,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
@@ -104,6 +112,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
@@ -115,7 +124,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: |
@@ -124,6 +136,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)
@@ -146,8 +159,6 @@ jobs:
- name: LZMA.
run: |
- %VC%
-
git clone %GIT%/telegramdesktop/lzma.git
cd lzma
cd C\Util\LzmaLib
@@ -162,8 +173,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
@@ -187,8 +196,6 @@ jobs:
- name: Zlib.
run: |
- %VC%
-
git clone %GIT%/telegramdesktop/zlib.git
cd zlib
git checkout tdesktop
@@ -198,8 +205,6 @@ jobs:
- name: MozJPEG.
shell: cmd
run: |
- %VC%
-
git clone -b v4.0.1-rc2 %GIT%/mozilla/mozjpeg.git
cd mozjpeg
cmake . ^
@@ -218,8 +223,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 .. ^
@@ -243,8 +246,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%
@@ -278,8 +279,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
@@ -288,10 +287,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
@@ -307,7 +303,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
@@ -328,8 +323,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
@@ -355,8 +348,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
@@ -408,7 +399,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%
@@ -426,8 +416,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
@@ -464,10 +452,12 @@ jobs:
echo "TDESKTOP_BUILD_DEFINE=$DEFINE" >> $GITHUB_ENV
- name: Free up some disk space.
+ working-directory: ${{ github.workspace }}
run: del /S *.pdb
- name: Kotatogram Desktop build.
if: env.ONLY_CACHE == 'false'
+ working-directory: ${{ github.workspace }}
run: |
cd %REPO_NAME%
call vcvarsall.bat x86 %SDK%
@@ -490,6 +480,7 @@ jobs:
- name: Move artifact.
if: env.UPLOAD_ARTIFACT == 'true'
+ working-directory: ${{ github.workspace }}
run: |
cd %REPO_NAME%\build
mkdir artifact
diff --git a/.gitmodules b/.gitmodules
index 3076c4da0..c7b202e09 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -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
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9ecc42a98..f54eae136 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,6 +29,7 @@ set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT T
get_filename_component(third_party_loc "Telegram/ThirdParty" REALPATH)
get_filename_component(submodules_loc "Telegram" REALPATH)
+get_filename_component(cmake_helpers_loc "cmake" REALPATH)
include(cmake/variables.cmake)
include(cmake/nice_target_sources.cmake)
@@ -37,6 +38,7 @@ include(cmake/target_link_frameworks.cmake)
include(cmake/init_target.cmake)
include(cmake/generate_target.cmake)
include(cmake/nuget.cmake)
+include(cmake/validate_d3d_compiler.cmake)
include(cmake/options.cmake)
diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index d8a19a8f2..2f5caf17f 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -82,50 +82,6 @@ PRIVATE
desktop-app::external_xxhash
)
-if (LINUX)
- target_link_libraries(Telegram
- PRIVATE
- desktop-app::external_glibmm
- desktop-app::external_glib
- )
-
- if (NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
- target_link_libraries(Telegram
- PRIVATE
- desktop-app::external_statusnotifieritem
- desktop-app::external_dbusmenu_qt
- )
- endif()
-
- if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
- target_link_libraries(Telegram
- PRIVATE
- desktop-app::external_xcb
- )
- endif()
-
- if (NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
- target_link_libraries(Telegram
- PRIVATE
- desktop-app::lib_waylandshells
- desktop-app::external_kwayland
- )
- endif()
-
- if (NOT DESKTOP_APP_DISABLE_GTK_INTEGRATION)
- find_package(PkgConfig REQUIRED)
-
- pkg_check_modules(GTK REQUIRED gtk+-3.0)
- target_include_directories(Telegram SYSTEM PRIVATE ${GTK_INCLUDE_DIRS})
-
- if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
- target_link_libraries(Telegram PRIVATE X11)
- endif()
-
- target_link_libraries(Telegram PRIVATE rt)
- endif()
-endif()
-
target_precompile_headers(Telegram PRIVATE ${src_loc}/stdafx.h)
nice_target_sources(Telegram ${src_loc}
PRIVATE
@@ -135,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
@@ -168,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
@@ -264,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
@@ -309,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
@@ -608,6 +573,8 @@ PRIVATE
history/view/history_view_cursor_state.h
history/view/history_view_element.cpp
history/view/history_view_element.h
+ history/view/history_view_empty_list_bubble.cpp
+ history/view/history_view_empty_list_bubble.h
history/view/history_view_group_call_tracker.cpp
history/view/history_view_group_call_tracker.h
history/view/history_view_list_widget.cpp
@@ -761,6 +728,10 @@ PRIVATE
lang/lang_numbers_animation.h
lang/lang_translator.cpp
lang/lang_translator.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
@@ -926,7 +897,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
@@ -941,7 +911,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
@@ -972,8 +941,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
@@ -1147,13 +1114,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
@@ -1180,8 +1145,6 @@ PRIVATE
config.h
facades.cpp
facades.h
- layout.cpp
- layout.h
logs.cpp
logs.h
main.cpp
@@ -1194,13 +1157,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
@@ -1331,6 +1287,48 @@ elseif (APPLE)
)
endif()
endif()
+else()
+ target_link_libraries(Telegram
+ PRIVATE
+ desktop-app::external_glibmm
+ desktop-app::external_glib
+ )
+
+ if (NOT DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
+ target_link_libraries(Telegram
+ PRIVATE
+ desktop-app::external_statusnotifieritem
+ desktop-app::external_dbusmenu_qt
+ )
+ endif()
+
+ if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
+ target_link_libraries(Telegram
+ PRIVATE
+ desktop-app::external_xcb
+ )
+ endif()
+
+ if (NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
+ target_link_libraries(Telegram
+ PRIVATE
+ desktop-app::lib_waylandshells
+ desktop-app::external_kwayland
+ )
+ endif()
+
+ if (NOT DESKTOP_APP_DISABLE_GTK_INTEGRATION)
+ find_package(PkgConfig REQUIRED)
+
+ pkg_check_modules(GTK REQUIRED gtk+-3.0)
+ target_include_directories(Telegram SYSTEM PRIVATE ${GTK_INCLUDE_DIRS})
+
+ if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
+ target_link_libraries(Telegram PRIVATE X11)
+ endif()
+
+ target_link_libraries(Telegram PRIVATE rt)
+ endif()
endif()
set(bundle_identifier "io.github.kotatogram")
@@ -1481,7 +1479,7 @@ if ((NOT DESKTOP_APP_DISABLE_AUTOUPDATE OR APPLE) AND NOT build_macstore AND NOT
endif()
if (DESKTOP_APP_SPECIAL_TARGET OR KTGDESKTOP_ENABLE_PACKER)
- add_executable(Packer WIN32)
+ add_executable(Packer)
init_target(Packer)
add_dependencies(Telegram Packer)
diff --git a/Telegram/Resources/art/background.jpg b/Telegram/Resources/art/background.jpg
new file mode 100644
index 000000000..04d57e4db
Binary files /dev/null and b/Telegram/Resources/art/background.jpg differ
diff --git a/Telegram/Resources/art/bg.jpg b/Telegram/Resources/art/bg.jpg
deleted file mode 100644
index 7fa05b3ed..000000000
Binary files a/Telegram/Resources/art/bg.jpg and /dev/null differ
diff --git a/Telegram/Resources/icons/calls/video_mini_invited.png b/Telegram/Resources/icons/calls/video_mini_invited.png
new file mode 100644
index 000000000..bd4cc48a8
Binary files /dev/null and b/Telegram/Resources/icons/calls/video_mini_invited.png differ
diff --git a/Telegram/Resources/icons/calls/video_mini_invited@2x.png b/Telegram/Resources/icons/calls/video_mini_invited@2x.png
new file mode 100644
index 000000000..cab3af0b1
Binary files /dev/null and b/Telegram/Resources/icons/calls/video_mini_invited@2x.png differ
diff --git a/Telegram/Resources/icons/calls/video_mini_invited@3x.png b/Telegram/Resources/icons/calls/video_mini_invited@3x.png
new file mode 100644
index 000000000..2a3d0be00
Binary files /dev/null and b/Telegram/Resources/icons/calls/video_mini_invited@3x.png differ
diff --git a/Telegram/Resources/icons/info_media_gif.png b/Telegram/Resources/icons/info_media_gif.png
index 4451f2ffd..3b44ac891 100644
Binary files a/Telegram/Resources/icons/info_media_gif.png and b/Telegram/Resources/icons/info_media_gif.png differ
diff --git a/Telegram/Resources/icons/info_media_gif@2x.png b/Telegram/Resources/icons/info_media_gif@2x.png
index 91adfd386..7a458e125 100644
Binary files a/Telegram/Resources/icons/info_media_gif@2x.png and b/Telegram/Resources/icons/info_media_gif@2x.png differ
diff --git a/Telegram/Resources/icons/info_media_gif@3x.png b/Telegram/Resources/icons/info_media_gif@3x.png
index 784c99723..304ecd2c3 100644
Binary files a/Telegram/Resources/icons/info_media_gif@3x.png and b/Telegram/Resources/icons/info_media_gif@3x.png differ
diff --git a/Telegram/Resources/langs/download.sh b/Telegram/Resources/langs/download.sh
deleted file mode 100755
index a4da69404..000000000
--- a/Telegram/Resources/langs/download.sh
+++ /dev/null
@@ -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
-
-cd $FullExecPath
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 65659e47b..dc1517cab 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -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)";
@@ -862,6 +874,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_photos#one" = "{count} photo";
"lng_profile_photos#other" = "{count} photos";
"lng_profile_photos_header" = "Photos";
+"lng_profile_gifs#one" = "{count} GIF";
+"lng_profile_gifs#other" = "{count} GIFs";
+"lng_profile_gifs_header" = "GIFs";
"lng_profile_videos#one" = "{count} video";
"lng_profile_videos#other" = "{count} videos";
"lng_profile_videos_header" = "Videos";
@@ -904,6 +919,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_info_add_as_contact" = "Add to contacts";
"lng_profile_shared_media" = "Shared media";
"lng_media_type_photos" = "Photos";
+"lng_media_type_gifs" = "GIFs";
"lng_media_type_videos" = "Videos";
"lng_media_type_songs" = "Audio files";
"lng_media_type_files" = "Files";
@@ -923,6 +939,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_export_channel" = "Export channel history";
"lng_media_selected_photo#one" = "{count} Photo";
"lng_media_selected_photo#other" = "{count} Photos";
+"lng_media_selected_gif#one" = "{count} GIF";
+"lng_media_selected_gif#other" = "{count} GIFs";
"lng_media_selected_video#one" = "{count} Video";
"lng_media_selected_video#other" = "{count} Videos";
"lng_media_selected_song#one" = "{count} Audio file";
@@ -936,6 +954,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_media_selected_link#one" = "{count} Shared link";
"lng_media_selected_link#other" = "{count} Shared links";
"lng_media_photo_empty" = "No photos here yet";
+"lng_media_gif_empty" = "No GIFs here yet";
"lng_media_video_empty" = "No videos here yet";
"lng_media_song_empty" = "No music files here yet";
"lng_media_file_empty" = "No files here yet";
@@ -1498,6 +1517,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_saved_forward_here" = "Forward messages here for quick access";
"lng_scheduled_messages" = "Scheduled Messages";
+"lng_scheduled_messages_empty" = "No scheduled messages here yet...";
"lng_reminder_messages" = "Reminders";
"lng_scheduled_date" = "Scheduled for {date}";
"lng_scheduled_date_until_online" = "Scheduled until online";
@@ -2005,6 +2025,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";
@@ -2084,6 +2105,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";
diff --git a/Telegram/Resources/langs/list b/Telegram/Resources/langs/list
deleted file mode 100644
index 658122760..000000000
--- a/Telegram/Resources/langs/list
+++ /dev/null
@@ -1 +0,0 @@
-de,es,it,ko,nl,pt_BR
diff --git a/Telegram/Resources/langs/rewrites/en.json b/Telegram/Resources/langs/rewrites/en.json
index befc59600..5eee92ced 100644
--- a/Telegram/Resources/langs/rewrites/en.json
+++ b/Telegram/Resources/langs/rewrites/en.json
@@ -159,24 +159,6 @@
"ktg_group_status_admin": "is admin",
"ktg_too_many_accounts_warning": "Warning! Using too many accounts at the same time is not recommended due to higher memory comsumption and possible crashes because of it.\n\nYou sure you want to add a new account?",
"ktg_account_add_anyway": "Add anyway",
- "ktg_media_selected_gif": {
- "zero": "{count} GIFs",
- "one": "{count} GIF",
- "two": "{count} GIFs",
- "few": "{count} GIFs",
- "many": "{count} GIFs",
- "other": "{count} GIFs"
- },
- "ktg_media_type_gif": "GIFs",
- "ktg_profile_gif": {
- "zero": "{count} GIFs",
- "one": "{count} GIF",
- "two": "{count} GIFs",
- "few": "{count} GIFs",
- "many": "{count} GIFs",
- "other": "{count} GIFs"
- },
- "ktg_media_gif_empty": "No GIFs here yet",
"ktg_rights_chat_send_stickers": "Send stickers",
"ktg_rights_chat_send_gif": "Send GIFs",
"ktg_rights_chat_send_games": "Send games",
diff --git a/Telegram/Resources/langs/upload.sh b/Telegram/Resources/langs/upload.sh
deleted file mode 100755
index fcbf4f2a0..000000000
--- a/Telegram/Resources/langs/upload.sh
+++ /dev/null
@@ -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
diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc
index bd5e298fe..bb6e245b7 100644
--- a/Telegram/Resources/qrc/telegram/telegram.qrc
+++ b/Telegram/Resources/qrc/telegram/telegram.qrc
@@ -42,7 +42,7 @@
../../export_html/js/script.js
- ../../art/bg.jpg
+ ../../art/background.jpg
../../art/bg_initial.jpg
../../art/logo_256.png
../../art/logo_256_blue.png
diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml
index 0bb3bb4cd..0e531cf95 100644
--- a/Telegram/Resources/uwp/AppX/AppxManifest.xml
+++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml
@@ -9,7 +9,7 @@
+ Version="2.9.3.0" />
Telegram Desktop
Telegram Messenger LLP
diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc
index f186a115f..4b9e417d8 100644
--- a/Telegram/Resources/winrc/Telegram.rc
+++ b/Telegram/Resources/winrc/Telegram.rc
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,8,11,0
- PRODUCTVERSION 2,8,11,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.11.0"
+ VALUE "FileVersion", "2.9.3.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
- VALUE "ProductVersion", "2.8.11.0"
+ VALUE "ProductVersion", "2.9.3.0"
END
END
BLOCK "VarFileInfo"
diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc
index 318d96f42..270c9ff3c 100644
--- a/Telegram/Resources/winrc/Updater.rc
+++ b/Telegram/Resources/winrc/Updater.rc
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,8,11,0
- PRODUCTVERSION 2,8,11,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.11.0"
+ VALUE "FileVersion", "2.9.3.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
- VALUE "ProductVersion", "2.8.11.0"
+ VALUE "ProductVersion", "2.9.3.0"
END
END
BLOCK "VarFileInfo"
diff --git a/Telegram/SourceFiles/_other/packer.cpp b/Telegram/SourceFiles/_other/packer.cpp
index 1b8789ecc..edd77f2b5 100644
--- a/Telegram/SourceFiles/_other/packer.cpp
+++ b/Telegram/SourceFiles/_other/packer.cpp
@@ -160,8 +160,7 @@ int main(int argc, char *argv[])
QString remove;
int version = 0;
- bool targetosx = false;
- bool targetwin64 = false;
+ [[maybe_unused]] bool targetwin64 = false;
QFileInfoList files;
for (int i = 0; i < argc; ++i) {
if (string("-path") == argv[i] && i + 1 < argc) {
@@ -170,7 +169,6 @@ int main(int argc, char *argv[])
files.push_back(info);
if (remove.isEmpty()) remove = info.canonicalPath() + "/";
} else if (string("-target") == argv[i] && i + 1 < argc) {
- targetosx = (string("osx") == argv[i + 1]);
targetwin64 = (string("win64") == argv[i + 1]);
} else if (string("-version") == argv[i] && i + 1 < argc) {
version = QString(argv[i + 1]).toInt();
@@ -498,7 +496,7 @@ int main(int argc, char *argv[])
#ifdef Q_OS_WIN
QString outName((targetwin64 ? QString("tx64upd%1") : QString("tupdate%1")).arg(AlphaVersion ? AlphaVersion : version));
#elif defined Q_OS_MAC
- QString outName((targetosx ? QString("tosxupd%1") : QString("tmacupd%1")).arg(AlphaVersion ? AlphaVersion : version));
+ QString outName(QString("tmacupd%1").arg(AlphaVersion ? AlphaVersion : version));
#elif defined Q_OS_UNIX
#ifndef _LP64
QString outName(QString("tlinux32upd%1").arg(AlphaVersion ? AlphaVersion : version));
diff --git a/Telegram/SourceFiles/api/api_blocked_peers.cpp b/Telegram/SourceFiles/api/api_blocked_peers.cpp
new file mode 100644
index 000000000..823147894
--- /dev/null
+++ b/Telegram/SourceFiles/api/api_blocked_peers.cpp
@@ -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 &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 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 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 peer, Fn 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 {
+ if (!_slice) {
+ reload();
+ }
+ return _slice
+ ? _changes.events_starting_with_copy(*_slice)
+ : (_changes.events() | rpl::type_erased());
+}
+
+void BlockedPeers::request(int offset, Fn 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
diff --git a/Telegram/SourceFiles/api/api_blocked_peers.h b/Telegram/SourceFiles/api/api_blocked_peers.h
new file mode 100644
index 000000000..ae506bbc9
--- /dev/null
+++ b/Telegram/SourceFiles/api/api_blocked_peers.h
@@ -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- list;
+ int total = 0;
+
+ bool operator==(const Slice &other) const;
+ bool operator!=(const Slice &other) const;
+ };
+
+ explicit BlockedPeers(not_null api);
+
+ void reload();
+ rpl::producer slice();
+ void request(int offset, Fn onDone);
+
+ void block(not_null peer);
+ void unblock(not_null peer, Fn onDone = nullptr);
+
+private:
+ const not_null _session;
+
+ MTP::Sender _api;
+
+ base::flat_map, mtpRequestId> _blockRequests;
+ mtpRequestId _requestId = 0;
+ std::optional _slice;
+ rpl::event_stream _changes;
+
+
+};
+
+} // namespace Api
diff --git a/Telegram/SourceFiles/api/api_bot.cpp b/Telegram/SourceFiles/api/api_bot.cpp
index 74704f27e..b10883016 100644
--- a/Telegram/SourceFiles/api/api_bot.cpp
+++ b/Telegram/SourceFiles/api/api_bot.cpp
@@ -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"
@@ -169,7 +170,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,
@@ -182,7 +183,7 @@ void SendBotCallbackDataWithPassword(
} else {
auto lifetime = std::make_shared();
button->requestId = -1;
- api->passwordState(
+ api->cloudPassword().state(
) | rpl::take(
1
) | rpl::start_with_next([=](const Core::CloudPasswordState &state) mutable {
diff --git a/Telegram/SourceFiles/api/api_cloud_password.cpp b/Telegram/SourceFiles/api/api_cloud_password.cpp
new file mode 100644
index 000000000..a8a14fd1d
--- /dev/null
+++ b/Telegram/SourceFiles/api/api_cloud_password.cpp
@@ -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 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::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 CloudPassword::state() const {
+ return _state
+ ? _stateChanges.events_starting_with_copy(*_state)
+ : (_stateChanges.events() | rpl::type_erased());
+}
+
+auto CloudPassword::stateCurrent() const
+-> std::optional {
+ return _state
+ ? base::make_optional(*_state)
+ : std::nullopt;
+}
+
+} // namespace Api
diff --git a/Telegram/SourceFiles/api/api_cloud_password.h b/Telegram/SourceFiles/api/api_cloud_password.h
new file mode 100644
index 000000000..2fdd7394c
--- /dev/null
+++ b/Telegram/SourceFiles/api/api_cloud_password.h
@@ -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 api);
+
+ void reload();
+ void applyPendingReset(const MTPaccount_ResetPasswordResult &data);
+ void clearUnconfirmedPassword();
+ rpl::producer state() const;
+ std::optional stateCurrent() const;
+
+private:
+ MTP::Sender _api;
+ mtpRequestId _requestId = 0;
+ std::unique_ptr _state;
+ rpl::event_stream _stateChanges;
+
+};
+
+} // namespace Api
diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp
index 1997e4c4f..6e6586a4f 100644
--- a/Telegram/SourceFiles/api/api_sending.cpp
+++ b/Telegram/SourceFiles/api/api_sending.cpp
@@ -41,22 +41,22 @@ namespace {
void InnerFillMessagePostFlags(
const Api::SendOptions &options,
not_null 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;
}
}
@@ -82,11 +82,10 @@ void SendExistingMedia(
session->data().nextLocalMessageId());
const auto randomId = openssl::RandomValue();
- 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();
@@ -114,19 +113,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,
@@ -292,11 +291,10 @@ bool SendDice(
const auto randomId = openssl::RandomValue();
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);
@@ -311,42 +309,26 @@ bool SendDice(
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(),
- MTP_int(views),
- MTP_int(forwards),
- MTPMessageReplies(),
- MTPint(), // edit_date
- MTP_string(messagePostAuthor),
- MTPlong(),
- //MTPMessageReactions(),
- MTPVector(),
- 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 finish) {
@@ -382,14 +364,15 @@ bool SendDice(
void FillMessagePostFlags(
const Api::SendAction &action,
not_null peer,
- MTPDmessage::Flags &flags) {
+ MessageFlags &flags) {
InnerFillMessagePostFlags(action.options, peer, flags);
}
void SendConfirmedFile(
not_null session,
const std::shared_ptr &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(
@@ -440,29 +423,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();
@@ -470,17 +448,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),
@@ -489,105 +487,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(),
- 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(),
- 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(),
- 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) {
diff --git a/Telegram/SourceFiles/api/api_sending.h b/Telegram/SourceFiles/api/api_sending.h
index 28a9431ee..1bd1b6f70 100644
--- a/Telegram/SourceFiles/api/api_sending.h
+++ b/Telegram/SourceFiles/api/api_sending.h
@@ -51,7 +51,7 @@ bool SendDice(
void FillMessagePostFlags(
const SendAction &action,
not_null peer,
- MTPDmessage::Flags &flags);
+ MessageFlags &flags);
void SendConfirmedFile(
not_null session,
diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp
index 314f65298..edd4ab362 100644
--- a/Telegram/SourceFiles/api/api_updates.cpp
+++ b/Telegram/SourceFiles/api/api_updates.cpp
@@ -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(),
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(),
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: {
diff --git a/Telegram/SourceFiles/api/api_user_privacy.cpp b/Telegram/SourceFiles/api/api_user_privacy.cpp
new file mode 100644
index 000000000..da5f9d190
--- /dev/null
+++ b/Telegram/SourceFiles/api/api_user_privacy.cpp
@@ -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;
+using TLRules = MTPVector;
+
+TLInputRules RulesToTL(const UserPrivacy::Rule &rule) {
+ const auto collectInputUsers = [](const auto &peers) {
+ auto result = QVector();
+ 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(); // #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();
+ 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(users)));
+ }
+ if (!chats.empty()) {
+ result.push_back(
+ MTP_inputPrivacyValueAllowChatParticipants(
+ MTP_vector(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(users)));
+ }
+ if (!chats.empty()) {
+ result.push_back(
+ MTP_inputPrivacyValueDisallowChatParticipants(
+ MTP_vector(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(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(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(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 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 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 {
+ 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
diff --git a/Telegram/SourceFiles/api/api_user_privacy.h b/Telegram/SourceFiles/api/api_user_privacy.h
new file mode 100644
index 000000000..a706cea02
--- /dev/null
+++ b/Telegram/SourceFiles/api/api_user_privacy.h
@@ -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> always;
+ std::vector> never;
+ bool ignoreAlways = false;
+ bool ignoreNever = false;
+ };
+
+ explicit UserPrivacy(not_null api);
+
+ void save(
+ Key key,
+ const UserPrivacy::Rule &rule);
+ void apply(
+ mtpTypeId type,
+ const MTPVector &rules,
+ bool allLoaded);
+
+ void reload(Key key);
+ rpl::producer value(Key key);
+
+private:
+ const not_null _session;
+
+ void pushPrivacy(Key key, const MTPVector &rules);
+
+ base::flat_map _privacySaveRequests;
+
+ base::flat_map _privacyRequestIds;
+ base::flat_map _privacyValues;
+ std::map> _privacyChanges;
+
+ MTP::Sender _api;
+
+};
+
+} // namespace Api
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index e80affd1f..abdae2379 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -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::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 session)
: MTP::Sender(&session->account().mtp())
, _session(session)
@@ -192,9 +134,12 @@ ApiWrap::ApiWrap(not_null session)
, _updateNotifySettingsTimer([=] { sendNotifySettingsUpdates(); })
, _authorizations(std::make_unique(this))
, _attachedStickers(std::make_unique(this))
+, _blockedPeers(std::make_unique(this))
+, _cloudPassword(std::make_unique(this))
, _selfDestruct(std::make_unique(this))
, _sensitiveContent(std::make_unique(this))
, _globalPrivacy(std::make_unique(this))
+, _userPrivacy(std::make_unique(this))
, _inviteLinks(std::make_unique(this)) {
crl::on_main(session, [=] {
// You can't use _session->lifetime() in the constructor,
@@ -2175,68 +2120,6 @@ void ApiWrap::leaveChannel(not_null channel) {
}
}
-void ApiWrap::blockPeer(not_null 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 peer, Fn 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) {
}
}
-void ApiWrap::savePrivacy(
- const MTPInputPrivacyKey &key,
- QVector &&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(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 &rules) {
- pushPrivacy(key, rules.v);
- if (key == Privacy::Key::LastSeen) {
- updatePrivacyLastSeens(rules.v);
- }
-}
-
-void ApiWrap::updatePrivacyLastSeens(const QVector &rules) {
+void ApiWrap::updatePrivacyLastSeens() {
const auto now = base::unixtime::now();
_session->data().enumerateUsers([&](UserData *user) {
if (user->isSelf() || !user->isFullLoaded()) {
@@ -3787,18 +3632,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;
@@ -3866,7 +3710,6 @@ void ApiWrap::forwardMessages(
history->addNewLocalMessage(
newId.msg,
flags,
- clientFlags,
HistoryItem::NewMessageDate(action.options.scheduled),
messageFromId,
messagePostAuthor,
@@ -3930,18 +3773,17 @@ void ApiWrap::forwardMessagesUnquoted(
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;
@@ -4044,15 +3886,14 @@ void ApiWrap::forwardMessagesUnquoted(
const auto forwards = 0;
const auto newGroupId = openssl::RandomValue();
- auto msgFlags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
- auto clientFlags = NewMessageClientFlags();
+ auto msgFlags = NewMessageFlags(peer);
FillMessagePostFlags(action, peer, msgFlags);
if (action.options.scheduled) {
- msgFlags |= MTPDmessage::Flag::f_from_scheduled;
+ msgFlags |= MessageFlag::IsOrWasScheduled;
} else {
- clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
+ msgFlags |= MessageFlag::LocalHistoryEntry;
}
for (auto i = fromIter, e = toIter; i != e; i++) {
@@ -4093,7 +3934,6 @@ void ApiWrap::forwardMessagesUnquoted(
history->addNewLocalMessage(
newId.msg,
msgFlags,
- clientFlags,
0, // viaBotId
0, // replyTo
HistoryItem::NewMessageDate(action.options.scheduled),
@@ -4107,7 +3947,6 @@ void ApiWrap::forwardMessagesUnquoted(
history->addNewLocalMessage(
newId.msg,
msgFlags,
- clientFlags,
0, // viaBotId
0, // replyTo
HistoryItem::NewMessageDate(action.options.scheduled),
@@ -4415,61 +4254,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(),
- MTP_int(views),
- MTP_int(forwards),
- MTPMessageReplies(),
- MTPint(), // edit_date
- MTP_string(messagePostAuthor),
- MTPlong(),
- //MTPMessageReactions(),
- MTPVector(),
- 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();
@@ -4678,11 +4500,10 @@ void ApiWrap::sendMessage(
_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);
@@ -4695,7 +4516,6 @@ void ApiWrap::sendMessage(
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);
@@ -4703,10 +4523,7 @@ void ApiWrap::sendMessage(
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);
@@ -4724,39 +4541,23 @@ void ApiWrap::sendMessage(
? _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;
- }
- 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(),
- MTPint()), // ttl_period
- clientFlags,
- NewMessageType::Unread);
+ flags |= MessageFlag::LocalHistoryEntry;
+ }
+ 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 finish) {
history->sendRequestId = request(MTPmessages_SendMessage(
MTP_flags(sendFlags),
@@ -4848,11 +4649,10 @@ void ApiWrap::sendInlineResult(
_session->data().nextLocalMessageId());
const auto randomId = openssl::RandomValue();
- 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();
@@ -4865,14 +4665,14 @@ void ApiWrap::sendInlineResult(
if (action.options.hideVia) {
sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_hide_via;
} else {
- 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();
@@ -4885,10 +4685,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 && !action.options.hideVia ? peerToUser(bot->id) : 0,
action.replyTo,
messagePostAuthor);
@@ -5290,52 +5089,6 @@ void ApiWrap::clearPeerPhoto(not_null 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::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 ApiWrap::passwordState() const {
- return _passwordState
- ? _passwordStateChanges.events_starting_with_copy(*_passwordState)
- : (_passwordStateChanges.events() | rpl::type_erased());
-}
-
-auto ApiWrap::passwordStateCurrent() const
--> std::optional {
- return _passwordState
- ? base::make_optional(*_passwordState)
- : std::nullopt;
-}
-
void ApiWrap::reloadContactSignupSilent() {
if (_contactSignupSilentRequestId) {
return;
@@ -5409,176 +5162,6 @@ void ApiWrap::saveSelfBio(const QString &text, FnMut 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 &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(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(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 &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 {
- 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 &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 {
- if (!_blockedPeersSlice) {
- reloadBlockedPeers();
- }
- return _blockedPeersSlice
- ? _blockedPeersChanges.events_starting_with_copy(*_blockedPeersSlice)
- : (_blockedPeersChanges.events() | rpl::type_erased());
-}
-
Api::Authorizations &ApiWrap::authorizations() {
return *_authorizations;
}
@@ -5587,6 +5170,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;
}
@@ -5599,6 +5190,10 @@ Api::GlobalPrivacy &ApiWrap::globalPrivacy() {
return *_globalPrivacy;
}
+Api::UserPrivacy &ApiWrap::userPrivacy() {
+ return *_userPrivacy;
+}
+
Api::InviteLinks &ApiWrap::inviteLinks() {
return *_inviteLinks;
}
diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h
index c6a084cfc..011541a4d 100644
--- a/Telegram/SourceFiles/apiwrap.h
+++ b/Telegram/SourceFiles/apiwrap.h
@@ -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> always;
- std::vector> never;
-
- static MTPInputPrivacyKey Input(Key key);
- static std::optional 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
- list;
- int total = 0;
-
- bool operator==(const BlockedPeersSlice &other) const;
- bool operator!=(const BlockedPeersSlice &other) const;
- };
-
explicit ApiWrap(not_null session);
~ApiWrap();
@@ -295,19 +254,10 @@ public:
void joinChannel(not_null channel);
void leaveChannel(not_null channel);
- void blockPeer(not_null peer);
- void unblockPeer(not_null peer, Fn onDone = nullptr);
-
void requestNotifySettings(const MTPInputNotifyPeer &peer);
void updateNotifySettingsDelayed(not_null peer);
void saveDraftToCloudDelayed(not_null history);
- void savePrivacy(
- const MTPInputPrivacyKey &key,
- QVector &&rules);
- void handlePrivacyChange(
- Privacy::Key key,
- const MTPVector &rules);
static int OnlineTillFromStatus(
const MTPUserStatus &status,
int currentOnlineTill);
@@ -440,11 +390,6 @@ public:
void uploadPeerPhoto(not_null peer, QImage &&image);
void clearPeerPhoto(not_null photo);
- void reloadPasswordState();
- void clearUnconfirmedPassword();
- rpl::producer passwordState() const;
- std::optional passwordStateCurrent() const;
-
void reloadContactSignupSilent();
rpl::producer contactSignupSilent() const;
std::optional contactSignupSilentCurrent() const;
@@ -452,17 +397,14 @@ public:
void saveSelfBio(const QString &text, FnMut done);
- void reloadPrivacy(Privacy::Key key);
- rpl::producer privacyValue(Privacy::Key key);
-
- void reloadBlockedPeers();
- rpl::producer 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(
@@ -476,6 +418,8 @@ public:
void closePoll(not_null item);
void reloadPollResults(not_null item);
+ void updatePrivacyLastSeens();
+
private:
struct MessageDataRequest {
using Callbacks = QList;
@@ -632,12 +576,6 @@ private:
void photoUploadReady(const FullMsgId &msgId, const MTPInputFile &file);
- Privacy parsePrivacy(const QVector &rules);
- void pushPrivacy(
- Privacy::Key key,
- const QVector &rules);
- void updatePrivacyLastSeens(const QVector &rules);
-
void migrateDone(
not_null peer,
not_null channel);
@@ -682,7 +620,6 @@ private:
QMap > _stickerSetRequests;
QMap _channelAmInRequests;
- base::flat_map, mtpRequestId> _blockRequests;
base::flat_map _notifySettingRequests;
base::flat_map, mtpRequestId> _draftsSaveRequestIds;
base::Timer _draftsSaveTimer;
@@ -707,8 +644,6 @@ private:
base::flat_map, StickersByEmoji> _stickersByEmoji;
- base::flat_map _privacySaveRequests;
-
mtpRequestId _contactsRequestId = 0;
mtpRequestId _contactsStatusesRequestId = 0;
@@ -771,27 +706,18 @@ private:
base::flat_map> _peerPhotoUploads;
- mtpRequestId _passwordRequestId = 0;
- std::unique_ptr _passwordState;
- rpl::event_stream _passwordStateChanges;
-
mtpRequestId _saveBioRequestId = 0;
FnMut _saveBioDone;
QString _saveBioText;
- base::flat_map _privacyRequestIds;
- base::flat_map _privacyValues;
- std::map> _privacyChanges;
-
- mtpRequestId _blockedPeersRequestId = 0;
- std::optional _blockedPeersSlice;
- rpl::event_stream _blockedPeersChanges;
-
const std::unique_ptr _authorizations;
const std::unique_ptr _attachedStickers;
+ const std::unique_ptr _blockedPeers;
+ const std::unique_ptr _cloudPassword;
const std::unique_ptr _selfDestruct;
const std::unique_ptr _sensitiveContent;
const std::unique_ptr _globalPrivacy;
+ const std::unique_ptr _userPrivacy;
const std::unique_ptr _inviteLinks;
base::flat_map _pollVotesRequestIds;
diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp
index 70c0fc178..ee0ef90cb 100644
--- a/Telegram/SourceFiles/app.cpp
+++ b/Telegram/SourceFiles/app.cpp
@@ -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 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();
}
diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h
index 9b82f25aa..6e581251b 100644
--- a/Telegram/SourceFiles/app.h
+++ b/Telegram/SourceFiles/app.h
@@ -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);
diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp
index 43d06d53a..dbd2a1822 100644
--- a/Telegram/SourceFiles/boxes/background_box.cpp
+++ b/Telegram/SourceFiles/boxes/background_box.cpp
@@ -239,8 +239,10 @@ void BackgroundBox::Inner::sortPapers() {
return std::make_tuple(
data.id() == current,
night ? data.isDark() : !data.isDark(),
- !data.isDefault() && !data.isLocal(),
- !data.isDefault() && data.isLocal());
+ Data::IsDefaultWallPaper(data),
+ !data.isDefault() && !Data::IsLegacy1DefaultWallPaper(data),
+ Data::IsLegacy2DefaultWallPaper(data),
+ Data::IsLegacy1DefaultWallPaper(data));
});
if (!_papers.empty() && _papers.front().data.id() == current) {
_papers.front().data = _papers.front().data.withParamsFrom(
@@ -366,6 +368,7 @@ void BackgroundBox::Inner::paintPaper(
_check->paint(p, checkLeft, checkTop, width());
} else if (Data::IsCloudWallPaper(paper.data)
&& !Data::IsDefaultWallPaper(paper.data)
+ && !Data::IsLegacy2DefaultWallPaper(paper.data)
&& !v::is_null(over)
&& (&paper == &_papers[getSelectionIndex(over)])) {
const auto deleteSelected = v::is(over);
@@ -395,6 +398,7 @@ void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) {
} else if (result >= _papers.size()) {
return Selection();
}
+ auto &data = _papers[result].data;
const auto deleteLeft = (column + 1) * (width + skip)
- st::stickerPanDeleteIconBg.width();
const auto deleteBottom = row * (height + skip) + skip
@@ -402,9 +406,10 @@ void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) {
const auto currentId = Window::Theme::Background()->id();
const auto inDelete = (x >= deleteLeft)
&& (y < deleteBottom)
- && Data::IsCloudWallPaper(_papers[result].data)
- && !Data::IsDefaultWallPaper(_papers[result].data)
- && (currentId != _papers[result].data.id());
+ && Data::IsCloudWallPaper(data)
+ && !Data::IsDefaultWallPaper(data)
+ && !Data::IsLegacy2DefaultWallPaper(data)
+ && (currentId != data.id());
return (result >= _papers.size())
? Selection()
: inDelete
diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp
index a85eb58e8..59d0759e1 100644
--- a/Telegram/SourceFiles/boxes/background_preview_box.cpp
+++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp
@@ -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);
}
@@ -622,6 +623,7 @@ void BackgroundPreviewBox::paintDate(Painter &p) {
if (!date || !_serviceBg) {
return;
}
+ auto hq = PainterHighQualityEnabler(p);
const auto text = date->text;
const auto bubbleHeight = st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom();
const auto bubbleTop = st::msgServiceMargin.top();
@@ -758,7 +760,7 @@ void BackgroundPreviewBox::checkLoadedDocument() {
};
_generating = Data::ReadImageAsync(
_media.get(),
- Window::Theme::ProcessBackgroundImage,
+ Window::Theme::PreprocessBackgroundImage,
generateCallback);
}
diff --git a/Telegram/SourceFiles/boxes/change_phone_box.cpp b/Telegram/SourceFiles/boxes/change_phone_box.cpp
index 199ebeab5..5de47051d 100644
--- a/Telegram/SourceFiles/boxes/change_phone_box.cpp
+++ b/Telegram/SourceFiles/boxes/change_phone_box.cpp
@@ -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(this, rpl::single(descriptionText), st::changePhoneLabel);
description->moveToLeft(st::boxPadding.left(), 0);
diff --git a/Telegram/SourceFiles/boxes/confirm_phone_box.cpp b/Telegram/SourceFiles/boxes/confirm_phone_box.cpp
index ca8d00aaa..51741745a 100644
--- a/Telegram/SourceFiles/boxes/confirm_phone_box.cpp
+++ b/Telegram/SourceFiles/boxes/confirm_phone_box.cpp
@@ -12,6 +12,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
@@ -19,7 +20,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"
@@ -297,7 +297,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);
@@ -348,7 +348,7 @@ void ConfirmPhoneBox::sendCode() {
void ConfirmPhoneBox::confirmDone(const MTPBool &result) {
_sendCodeRequestId = 0;
- Ui::show(Box(tr::lng_confirm_phone_success(tr::now, lt_phone, App::formatPhone(_phone))));
+ Ui::show(Box(tr::lng_confirm_phone_success(tr::now, lt_phone, Ui::FormatPhone(_phone))));
}
void ConfirmPhoneBox::confirmFail(const MTP::Error &error) {
diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
index 9d8f90517..3b198bb4c 100644
--- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp
+++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
@@ -101,6 +101,11 @@ Ui::AlbumType ComputeAlbumType(not_null item) {
return Ui::AlbumType();
}
+bool CanBeCompressed(Ui::AlbumType type) {
+ return (type == Ui::AlbumType::None)
+ || (type == Ui::AlbumType::PhotoVideo);
+}
+
} // namespace
EditCaptionBox::EditCaptionBox(
@@ -170,7 +175,7 @@ void EditCaptionBox::rebuildPreview() {
const auto photo = media->photo();
const auto document = media->document();
if (photo || document->isVideoFile() || document->isAnimation()) {
- _isPhoto = true;
+ _isPhoto = (photo != nullptr);
const auto media = Ui::CreateChild(
this,
gifPaused,
@@ -230,6 +235,8 @@ void EditCaptionBox::rebuildPreview() {
_scroll->setOwnedWidget(
object_ptr::fromRaw(_content.get()));
+ _previewRebuilds.fire({});
+
captionResized();
}
@@ -302,10 +309,11 @@ void EditCaptionBox::setupShadows() {
}
void EditCaptionBox::setupControls() {
- auto hintLabelToggleOn = _isPhoto.value(
- ) | rpl::map([=](bool value) {
+ auto hintLabelToggleOn = _previewRebuilds.events_starting_with(
+ rpl::empty_value()
+ ) | rpl::map([=] {
return _controller->session().settings().photoEditorHintShown()
- ? value
+ ? _isPhoto
: false;
});
@@ -327,9 +335,12 @@ void EditCaptionBox::setupControls() {
st::defaultBoxCheckbox),
st::editMediaCheckboxMargins)
)->toggleOn(
- _isPhoto.value(
- ) | rpl::map([=](bool value) {
- return value && (_albumType == Ui::AlbumType::None);
+ _previewRebuilds.events_starting_with(
+ rpl::empty_value()
+ ) | rpl::map([=] {
+ return _isPhoto
+ && CanBeCompressed(_albumType)
+ && !_preparedList.files.empty();
}),
anim::type::instant
)->entity()->checkedChanges(
@@ -610,6 +621,7 @@ void EditCaptionBox::resizeEvent(QResizeEvent *e) {
_emojiToggle->update();
if (!_controls->isHidden()) {
+ _controls->resizeToWidth(width());
_controls->moveToLeft(
st::boxPhotoPadding.left(),
bottom - _controls->heightNoMargins());
@@ -661,7 +673,7 @@ void EditCaptionBox::save() {
_controller->session().api().editMedia(
std::move(_preparedList),
- (!_asFile && _isPhoto.current())
+ (!_asFile && _isPhoto && CanBeCompressed(_albumType))
? SendMediaType::Photo
: SendMediaType::File,
_field->getTextWithAppliedMarkdown(),
diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.h b/Telegram/SourceFiles/boxes/edit_caption_box.h
index c145f285c..02b364881 100644
--- a/Telegram/SourceFiles/boxes/edit_caption_box.h
+++ b/Telegram/SourceFiles/boxes/edit_caption_box.h
@@ -74,14 +74,14 @@ private:
const not_null _controller;
const not_null _historyItem;
- const bool _isAllowedEditMedia = false;
+ const bool _isAllowedEditMedia;
const Ui::AlbumType _albumType;
const base::unique_qptr _controls;
const base::unique_qptr _scroll;
const base::unique_qptr _field;
const base::unique_qptr _emojiToggle;
- const base::unique_qptr _topShadow,_bottomShadow;
+ const base::unique_qptr _topShadow, _bottomShadow;
base::unique_qptr _content;
base::unique_qptr _emojiPanel;
@@ -93,15 +93,16 @@ private:
mtpRequestId _saveRequestId = 0;
+ bool _isPhoto = false;
bool _asFile = false;
QString _error;
- rpl::variable _isPhoto = false;
rpl::variable _footerHeight = 0;
rpl::event_stream<> _editMediaClicks;
rpl::event_stream<> _photoEditorOpens;
+ rpl::event_stream<> _previewRebuilds;
rpl::event_stream _contentHeight;
};
diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
index 1102f5fa1..c223e51c5 100644
--- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
+++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp
@@ -170,63 +170,6 @@ void EditPrivacyBox::editExceptions(
Ui::LayerOption::KeepOther);
}
-QVector EditPrivacyBox::collectResult() {
- const auto collectInputUsers = [](const auto &peers) {
- auto result = QVector();
- 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(); // #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();
- 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(users)));
- }
- if (!chats.empty()) {
- result.push_back(MTP_inputPrivacyValueAllowChatParticipants(MTP_vector(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(users)));
- }
- if (!chats.empty()) {
- result.push_back(MTP_inputPrivacyValueDisallowChatParticipants(MTP_vector(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> &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();
}));
});
diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.h b/Telegram/SourceFiles/boxes/edit_privacy_box.h
index 4310d1b51..d414c9318 100644
--- a/Telegram/SourceFiles/boxes/edit_privacy_box.h
+++ b/Telegram/SourceFiles/boxes/edit_privacy_box.h
@@ -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 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 collectResult();
Ui::FlatLabel *addLabel(
not_null container,
diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp
index f5aa0a7e3..18545d2d1 100644
--- a/Telegram/SourceFiles/boxes/passcode_box.cpp
+++ b/Telegram/SourceFiles/boxes/passcode_box.cpp
@@ -12,8 +12,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"
@@ -43,7 +45,7 @@ enum class PasswordErrorType {
void SetCloudPassword(
not_null box,
not_null session) {
- session->api().passwordState(
+ session->api().cloudPassword().state(
) | rpl::start_with_next([=] {
using namespace Settings;
const auto weak = Ui::MakeWeak(box);
@@ -96,6 +98,56 @@ void TransferPasswordError(
}
}
+void StartPendingReset(
+ not_null session,
+ not_null context,
+ Fn 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(
+ 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("Error: " + error.type()));
+ strong->closeBox();
+ }
+ close();
+ }).send();
+}
+
} // namespace
PasscodeBox::CloudFields PasscodeBox::CloudFields::From(
@@ -107,6 +159,7 @@ PasscodeBox::CloudFields PasscodeBox::CloudFields::From(
result.hasRecovery = current.hasRecovery;
result.notEmptyPassport = current.notEmptyPassport;
result.hint = current.hint;
+ result.pendingResetDate = current.pendingResetDate;
return result;
}
@@ -146,7 +199,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()) {
@@ -204,14 +258,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
@@ -238,7 +292,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);
@@ -286,7 +342,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());
@@ -318,7 +374,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());
@@ -380,7 +436,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();
@@ -914,7 +970,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();
@@ -923,7 +979,7 @@ void PasscodeBox::badOldPasscode() {
void PasscodeBox::oldChanged() {
if (!_oldError.isEmpty()) {
_oldError = QString();
- if (_cloudFields.hasRecovery && _hintText.isEmpty()) {
+ if (_showRecoverLink && _hintText.isEmpty()) {
_recover->show();
}
update();
@@ -945,7 +1001,21 @@ void PasscodeBox::emailChanged() {
}
void PasscodeBox::recoverByEmail() {
- if (_pattern.isEmpty()) {
+ if (!_cloudFields.hasRecovery) {
+ const auto session = _session;
+ const auto confirmBox = std::make_shared>();
+ const auto reset = crl::guard(this, [=] {
+ StartPendingReset(session, this, [=] {
+ if (const auto box = *confirmBox) {
+ box->closeBox();
+ }
+ });
+ });
+ *confirmBox = getDelegate()->show(Box(
+ 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) {
@@ -965,10 +1035,13 @@ void PasscodeBox::recoverExpired() {
void PasscodeBox::recover() {
if (_pattern == "-") return;
+ const auto weak = Ui::MakeWeak(this);
const auto box = getDelegate()->show(Box(
_session,
_pattern,
- _cloudFields.notEmptyPassport));
+ _cloudFields.notEmptyPassport,
+ _cloudFields.pendingResetDate != 0,
+ [weak] { if (weak) { weak->closeBox(); } }));
box->passwordCleared(
) | rpl::map_to(
@@ -997,11 +1070,37 @@ RecoverBox::RecoverBox(
QWidget*,
not_null session,
const QString &pattern,
- bool notEmptyPassport)
+ bool notEmptyPassport,
+ bool hasPendingReset,
+ Fn 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>();
+ 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(
+ tr::lng_cloud_password_reset_with_email(tr::now),
+ tr::lng_cloud_password_reset_ok(tr::now),
+ reset));
+ });
+ }
}
rpl::producer<> RecoverBox::passwordCleared() const {
@@ -1018,7 +1117,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(); });
@@ -1045,6 +1150,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() {
@@ -1086,11 +1194,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;
@@ -1103,8 +1218,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;
}
@@ -1122,18 +1236,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();
}
}
diff --git a/Telegram/SourceFiles/boxes/passcode_box.h b/Telegram/SourceFiles/boxes/passcode_box.h
index 875bed383..1d17f5fab 100644
--- a/Telegram/SourceFiles/boxes/passcode_box.h
+++ b/Telegram/SourceFiles/boxes/passcode_box.h
@@ -39,6 +39,7 @@ public:
QString hint;
Core::SecureSecretAlgo newSecureSecretAlgo;
bool turningOff = false;
+ TimeId pendingResetDate = 0;
// Check cloud password for some action.
Fn customCheckCallback;
@@ -157,6 +158,7 @@ private:
object_ptr _passwordHint;
object_ptr _recoverEmail;
object_ptr _recover;
+ bool _showRecoverLink = false;
QString _oldError, _newError, _emailError;
@@ -172,7 +174,9 @@ public:
QWidget*,
not_null session,
const QString &pattern,
- bool notEmptyPassport);
+ bool notEmptyPassport,
+ bool hasPendingReset,
+ Fn 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 _recoverCode;
+ object_ptr _noEmailAccess;
+ Fn _closeParent;
QString _error;
diff --git a/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp
index 8b6eb1404..9bb82ae36 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp
@@ -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);
}
diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
index 808dbd228..f7fb6778a 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
@@ -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"
@@ -462,7 +463,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(),
@@ -513,7 +514,7 @@ void EditAdminBox::transferOwnershipChecked() {
}
void EditAdminBox::requestTransferPassword(not_null channel) {
- peer()->session().api().passwordState(
+ peer()->session().api().cloudPassword().state(
) | rpl::take(
1
) | rpl::start_with_next([=](const Core::CloudPasswordState &state) {
diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp
index c68f9c1ea..480c37fad 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp
@@ -1472,9 +1472,27 @@ base::unique_qptr ParticipantsBoxController::rowContextMenu(
crl::guard(this, [=] {
_navigation->showPeerInfo(participant); }));
}
- result->addAction(
- ktr("ktg_context_show_messages_from"),
- crl::guard(this, [=] { App::searchByHashtag(QString(), _peer, user); }));
+ if (const auto window = App::wnd()) {
+ if (const auto mainwidget = window->sessionContent()) {
+ result->addAction(
+ ktr("ktg_context_show_messages_from"),
+ crl::guard(this, [=] {
+ mainwidget->searchMessages(
+ " ",
+ (_peer && !_peer->isUser())
+ ? _peer->owner().history(_peer).get()
+ : Dialogs::Key(),
+ user);
+ }));
+ if (const auto openedPeer = mainwidget->peer()) {
+ if (openedPeer->canWrite()) {
+ result->addAction(
+ ktr("ktg_profile_mention_user"),
+ crl::guard(this, [=] { mainwidget->mentionUser(user); }));
+ }
+ }
+ }
+ }
if (_role == Role::Kicked) {
if (_peer->isMegagroup()
&& _additional.canRestrictParticipant(participant)) {
@@ -1489,17 +1507,6 @@ base::unique_qptr ParticipantsBoxController::rowContextMenu(
}
return result;
}
- if (const auto window = App::wnd()) {
- if (const auto mainwidget = window->sessionContent()) {
- if (const auto openedPeer = mainwidget->peer()) {
- if (openedPeer->canWrite()) {
- result->addAction(
- ktr("ktg_profile_mention_user"),
- crl::guard(this, [=] { mainwidget->mentionUser(user); }));
- }
- }
- }
- }
if (user && _additional.canAddOrEditAdmin(user)) {
const auto isAdmin = _additional.isCreator(user)
|| _additional.adminRights(user).has_value();
diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style
index eaa14ba75..ddbc02737 100644
--- a/Telegram/SourceFiles/calls/calls.style
+++ b/Telegram/SourceFiles/calls/calls.style
@@ -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;
diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp
index 6e828bb23..a992ed546 100644
--- a/Telegram/SourceFiles/calls/calls_box_controller.cpp
+++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp
@@ -406,7 +406,7 @@ void BoxController::receivedCalls(const QVector &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 {
diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp
index 41b5beb92..af7922578 100644
--- a/Telegram/SourceFiles/calls/calls_call.cpp
+++ b/Telegram/SourceFiles/calls/calls_call.cpp
@@ -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 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);
diff --git a/Telegram/SourceFiles/calls/calls_call.h b/Telegram/SourceFiles/calls/calls_call.h
index fec90fc4a..1478a8ed8 100644
--- a/Telegram/SourceFiles/calls/calls_call.h
+++ b/Telegram/SourceFiles/calls/calls_call.h
@@ -77,8 +77,10 @@ public:
Fn onSuccess,
bool video) = 0;
- virtual auto callGetVideoCapture()
- -> std::shared_ptr = 0;
+ virtual auto callGetVideoCapture(
+ const QString &deviceId,
+ bool isScreenCapture)
+ -> std::shared_ptr = 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 uniqueId);
+
[[nodiscard]] rpl::lifetime &lifetime() {
return _lifetime;
}
@@ -268,6 +281,8 @@ private:
std::unique_ptr _instance;
std::shared_ptr _videoCapture;
+ QString _videoCaptureDeviceId;
+ bool _videoCaptureIsScreencast = false;
const std::unique_ptr _videoIncoming;
const std::unique_ptr _videoOutgoing;
diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp
index 56fd9a6d1..d23c625ab 100644
--- a/Telegram/SourceFiles/calls/calls_instance.cpp
+++ b/Telegram/SourceFiles/calls/calls_instance.cpp
@@ -65,8 +65,10 @@ public:
Fn onSuccess,
bool video) override;
void callPlaySound(CallSound sound) override;
- auto callGetVideoCapture()
- -> std::shared_ptr override;
+ auto callGetVideoCapture(
+ const QString &deviceId,
+ bool isScreenCapture)
+ -> std::shared_ptr override;
void groupCallFinished(not_null call) override;
void groupCallFailed(not_null call) override;
@@ -124,9 +126,11 @@ void Instance::Delegate::callPlaySound(CallSound sound) {
}());
}
-auto Instance::Delegate::callGetVideoCapture()
+auto Instance::Delegate::callGetVideoCapture(
+ const QString &deviceId,
+ bool isScreenCapture)
-> std::shared_ptr {
- return _instance->getVideoCapture();
+ return _instance->getVideoCapture(deviceId, isScreenCapture);
}
void Instance::Delegate::groupCallFinished(not_null call) {
@@ -160,7 +164,7 @@ void Instance::Delegate::groupCallPlaySound(GroupCallSound sound) {
auto Instance::Delegate::groupCallGetVideoCapture(const QString &deviceId)
-> std::shared_ptr {
- return _instance->getVideoCapture(deviceId);
+ return _instance->getVideoCapture(deviceId, false);
}
FnMut Instance::Delegate::groupCallAddAsyncWaiter() {
@@ -700,18 +704,25 @@ void Instance::requestPermissionOrFail(Platform::PermissionType type, Fn
}
std::shared_ptr Instance::getVideoCapture(
- QString deviceId) {
- if (deviceId.isEmpty()) {
- deviceId = Core::App().settings().callVideoInputDeviceId();
- }
+ std::optional 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::Create(
tgcalls::StaticThreads::getThreads(),
- deviceId.toStdString()));
+ startDeviceId.toStdString()));
_videoCapture = result;
return result;
}
diff --git a/Telegram/SourceFiles/calls/calls_instance.h b/Telegram/SourceFiles/calls/calls_instance.h
index 6ab7e937c..c403ef4eb 100644
--- a/Telegram/SourceFiles/calls/calls_instance.h
+++ b/Telegram/SourceFiles/calls/calls_instance.h
@@ -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 deviceId = std::nullopt,
+ bool isScreenCapture = false)
-> std::shared_ptr;
void requestPermissionsOrFail(Fn onSuccess, bool video = true);
diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp
index 71e875584..f66ede2c8 100644
--- a/Telegram/SourceFiles/calls/calls_panel.cpp
+++ b/Telegram/SourceFiles/calls/calls_panel.cpp
@@ -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)
, _user(call->user())
+, _layerBg(std::make_unique